UNPKG

863 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2021, 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 util from 'lodash.debounce';
24import Heap from 'heap';
25
26function _typeof(obj) {
27 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
28 _typeof = function (obj) {
29 return typeof obj;
30 };
31 } else {
32 _typeof = function (obj) {
33 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
34 };
35 }
36
37 return _typeof(obj);
38}
39
40function _classCallCheck(instance, Constructor) {
41 if (!(instance instanceof Constructor)) {
42 throw new TypeError("Cannot call a class as a function");
43 }
44}
45
46function _defineProperties(target, props) {
47 for (var i = 0; i < props.length; i++) {
48 var descriptor = props[i];
49 descriptor.enumerable = descriptor.enumerable || false;
50 descriptor.configurable = true;
51 if ("value" in descriptor) descriptor.writable = true;
52 Object.defineProperty(target, descriptor.key, descriptor);
53 }
54}
55
56function _createClass(Constructor, protoProps, staticProps) {
57 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
58 if (staticProps) _defineProperties(Constructor, staticProps);
59 return Constructor;
60}
61
62function _defineProperty(obj, key, value) {
63 if (key in obj) {
64 Object.defineProperty(obj, key, {
65 value: value,
66 enumerable: true,
67 configurable: true,
68 writable: true
69 });
70 } else {
71 obj[key] = value;
72 }
73
74 return obj;
75}
76
77function _slicedToArray(arr, i) {
78 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
79}
80
81function _arrayWithHoles(arr) {
82 if (Array.isArray(arr)) return arr;
83}
84
85function _iterableToArrayLimit(arr, i) {
86 var _arr = [];
87 var _n = true;
88 var _d = false;
89 var _e = undefined;
90
91 try {
92 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
93 _arr.push(_s.value);
94
95 if (i && _arr.length === i) break;
96 }
97 } catch (err) {
98 _d = true;
99 _e = err;
100 } finally {
101 try {
102 if (!_n && _i["return"] != null) _i["return"]();
103 } finally {
104 if (_d) throw _e;
105 }
106 }
107
108 return _arr;
109}
110
111function _nonIterableRest() {
112 throw new TypeError("Invalid attempt to destructure non-iterable instance");
113}
114
115var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
116
117var navigator = window$1 ? window$1.navigator : null;
118var document$1 = window$1 ? window$1.document : null;
119
120var typeofstr = _typeof('');
121
122var typeofobj = _typeof({});
123
124var typeoffn = _typeof(function () {});
125
126var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
127
128var instanceStr = function instanceStr(obj) {
129 return obj && obj.instanceString && fn(obj.instanceString) ? obj.instanceString() : null;
130};
131
132var string = function string(obj) {
133 return obj != null && _typeof(obj) == typeofstr;
134};
135var fn = function fn(obj) {
136 return obj != null && _typeof(obj) === typeoffn;
137};
138var array = function array(obj) {
139 return !elementOrCollection(obj) && (Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array);
140};
141var plainObject = function plainObject(obj) {
142 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
143};
144var object = function object(obj) {
145 return obj != null && _typeof(obj) === typeofobj;
146};
147var number = function number(obj) {
148 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
149};
150var integer = function integer(obj) {
151 return number(obj) && Math.floor(obj) === obj;
152};
153var htmlElement = function htmlElement(obj) {
154 if ('undefined' === typeofhtmlele) {
155 return undefined;
156 } else {
157 return null != obj && obj instanceof HTMLElement;
158 }
159};
160var elementOrCollection = function elementOrCollection(obj) {
161 return element(obj) || collection(obj);
162};
163var element = function element(obj) {
164 return instanceStr(obj) === 'collection' && obj._private.single;
165};
166var collection = function collection(obj) {
167 return instanceStr(obj) === 'collection' && !obj._private.single;
168};
169var core = function core(obj) {
170 return instanceStr(obj) === 'core';
171};
172var stylesheet = function stylesheet(obj) {
173 return instanceStr(obj) === 'stylesheet';
174};
175var event = function event(obj) {
176 return instanceStr(obj) === 'event';
177};
178var emptyString = function emptyString(obj) {
179 if (obj === undefined || obj === null) {
180 // null is empty
181 return true;
182 } else if (obj === '' || obj.match(/^\s+$/)) {
183 return true; // empty string is empty
184 }
185
186 return false; // otherwise, we don't know what we've got
187};
188var domElement = function domElement(obj) {
189 if (typeof HTMLElement === 'undefined') {
190 return false; // we're not in a browser so it doesn't matter
191 } else {
192 return obj instanceof HTMLElement;
193 }
194};
195var boundingBox = function boundingBox(obj) {
196 return plainObject(obj) && number(obj.x1) && number(obj.x2) && number(obj.y1) && number(obj.y2);
197};
198var promise = function promise(obj) {
199 return object(obj) && fn(obj.then);
200};
201var ms = function ms() {
202 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
203}; // probably a better way to detect this...
204
205var memoize = function memoize(fn, keyFn) {
206 if (!keyFn) {
207 keyFn = function keyFn() {
208 if (arguments.length === 1) {
209 return arguments[0];
210 } else if (arguments.length === 0) {
211 return 'undefined';
212 }
213
214 var args = [];
215
216 for (var i = 0; i < arguments.length; i++) {
217 args.push(arguments[i]);
218 }
219
220 return args.join('$');
221 };
222 }
223
224 var memoizedFn = function memoizedFn() {
225 var self = this;
226 var args = arguments;
227 var ret;
228 var k = keyFn.apply(self, args);
229 var cache = memoizedFn.cache;
230
231 if (!(ret = cache[k])) {
232 ret = cache[k] = fn.apply(self, args);
233 }
234
235 return ret;
236 };
237
238 memoizedFn.cache = {};
239 return memoizedFn;
240};
241
242var camel2dash = memoize(function (str) {
243 return str.replace(/([A-Z])/g, function (v) {
244 return '-' + v.toLowerCase();
245 });
246});
247var dash2camel = memoize(function (str) {
248 return str.replace(/(-\w)/g, function (v) {
249 return v[1].toUpperCase();
250 });
251});
252var prependCamel = memoize(function (prefix, str) {
253 return prefix + str[0].toUpperCase() + str.substring(1);
254}, function (prefix, str) {
255 return prefix + '$' + str;
256});
257var capitalize = function capitalize(str) {
258 if (emptyString(str)) {
259 return str;
260 }
261
262 return str.charAt(0).toUpperCase() + str.substring(1);
263};
264
265var number$1 = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
266var rgba = 'rgb[a]?\\((' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)(?:\\s*,\\s*(' + number$1 + '))?\\)';
267var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
268var hsla = 'hsl[a]?\\((' + number$1 + ')\\s*,\\s*(' + number$1 + '[%])\\s*,\\s*(' + number$1 + '[%])(?:\\s*,\\s*(' + number$1 + '))?\\)';
269var hslaNoBackRefs = 'hsl[a]?\\((?:' + number$1 + ')\\s*,\\s*(?:' + number$1 + '[%])\\s*,\\s*(?:' + number$1 + '[%])(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
270var hex3 = '\\#[0-9a-fA-F]{3}';
271var hex6 = '\\#[0-9a-fA-F]{6}';
272
273var ascending = function ascending(a, b) {
274 if (a < b) {
275 return -1;
276 } else if (a > b) {
277 return 1;
278 } else {
279 return 0;
280 }
281};
282var descending = function descending(a, b) {
283 return -1 * ascending(a, b);
284};
285
286var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
287 var args = arguments;
288
289 for (var i = 1; i < args.length; i++) {
290 var obj = args[i];
291
292 if (obj == null) {
293 continue;
294 }
295
296 var keys = Object.keys(obj);
297
298 for (var j = 0; j < keys.length; j++) {
299 var k = keys[j];
300 tgt[k] = obj[k];
301 }
302 }
303
304 return tgt;
305};
306
307var hex2tuple = function hex2tuple(hex) {
308 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
309 return;
310 }
311
312 var shortHex = hex.length === 4;
313 var r, g, b;
314 var base = 16;
315
316 if (shortHex) {
317 r = parseInt(hex[1] + hex[1], base);
318 g = parseInt(hex[2] + hex[2], base);
319 b = parseInt(hex[3] + hex[3], base);
320 } else {
321 r = parseInt(hex[1] + hex[2], base);
322 g = parseInt(hex[3] + hex[4], base);
323 b = parseInt(hex[5] + hex[6], base);
324 }
325
326 return [r, g, b];
327}; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
328
329var hsl2tuple = function hsl2tuple(hsl) {
330 var ret;
331 var h, s, l, a, r, g, b;
332
333 function hue2rgb(p, q, t) {
334 if (t < 0) t += 1;
335 if (t > 1) t -= 1;
336 if (t < 1 / 6) return p + (q - p) * 6 * t;
337 if (t < 1 / 2) return q;
338 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
339 return p;
340 }
341
342 var m = new RegExp('^' + hsla + '$').exec(hsl);
343
344 if (m) {
345 // get hue
346 h = parseInt(m[1]);
347
348 if (h < 0) {
349 h = (360 - -1 * h % 360) % 360;
350 } else if (h > 360) {
351 h = h % 360;
352 }
353
354 h /= 360; // normalise on [0, 1]
355
356 s = parseFloat(m[2]);
357
358 if (s < 0 || s > 100) {
359 return;
360 } // saturation is [0, 100]
361
362
363 s = s / 100; // normalise on [0, 1]
364
365 l = parseFloat(m[3]);
366
367 if (l < 0 || l > 100) {
368 return;
369 } // lightness is [0, 100]
370
371
372 l = l / 100; // normalise on [0, 1]
373
374 a = m[4];
375
376 if (a !== undefined) {
377 a = parseFloat(a);
378
379 if (a < 0 || a > 1) {
380 return;
381 } // alpha is [0, 1]
382
383 } // now, convert to rgb
384 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
385
386
387 if (s === 0) {
388 r = g = b = Math.round(l * 255); // achromatic
389 } else {
390 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
391 var p = 2 * l - q;
392 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
393 g = Math.round(255 * hue2rgb(p, q, h));
394 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
395 }
396
397 ret = [r, g, b, a];
398 }
399
400 return ret;
401}; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
402
403var rgb2tuple = function rgb2tuple(rgb) {
404 var ret;
405 var m = new RegExp('^' + rgba + '$').exec(rgb);
406
407 if (m) {
408 ret = [];
409 var isPct = [];
410
411 for (var i = 1; i <= 3; i++) {
412 var channel = m[i];
413
414 if (channel[channel.length - 1] === '%') {
415 isPct[i] = true;
416 }
417
418 channel = parseFloat(channel);
419
420 if (isPct[i]) {
421 channel = channel / 100 * 255; // normalise to [0, 255]
422 }
423
424 if (channel < 0 || channel > 255) {
425 return;
426 } // invalid channel value
427
428
429 ret.push(Math.floor(channel));
430 }
431
432 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
433 var allArePct = isPct[1] && isPct[2] && isPct[3];
434
435 if (atLeastOneIsPct && !allArePct) {
436 return;
437 } // must all be percent values if one is
438
439
440 var alpha = m[4];
441
442 if (alpha !== undefined) {
443 alpha = parseFloat(alpha);
444
445 if (alpha < 0 || alpha > 1) {
446 return;
447 } // invalid alpha value
448
449
450 ret.push(alpha);
451 }
452 }
453
454 return ret;
455};
456var colorname2tuple = function colorname2tuple(color) {
457 return colors[color.toLowerCase()];
458};
459var color2tuple = function color2tuple(color) {
460 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
461};
462var colors = {
463 // special colour names
464 transparent: [0, 0, 0, 0],
465 // NB alpha === 0
466 // regular colours
467 aliceblue: [240, 248, 255],
468 antiquewhite: [250, 235, 215],
469 aqua: [0, 255, 255],
470 aquamarine: [127, 255, 212],
471 azure: [240, 255, 255],
472 beige: [245, 245, 220],
473 bisque: [255, 228, 196],
474 black: [0, 0, 0],
475 blanchedalmond: [255, 235, 205],
476 blue: [0, 0, 255],
477 blueviolet: [138, 43, 226],
478 brown: [165, 42, 42],
479 burlywood: [222, 184, 135],
480 cadetblue: [95, 158, 160],
481 chartreuse: [127, 255, 0],
482 chocolate: [210, 105, 30],
483 coral: [255, 127, 80],
484 cornflowerblue: [100, 149, 237],
485 cornsilk: [255, 248, 220],
486 crimson: [220, 20, 60],
487 cyan: [0, 255, 255],
488 darkblue: [0, 0, 139],
489 darkcyan: [0, 139, 139],
490 darkgoldenrod: [184, 134, 11],
491 darkgray: [169, 169, 169],
492 darkgreen: [0, 100, 0],
493 darkgrey: [169, 169, 169],
494 darkkhaki: [189, 183, 107],
495 darkmagenta: [139, 0, 139],
496 darkolivegreen: [85, 107, 47],
497 darkorange: [255, 140, 0],
498 darkorchid: [153, 50, 204],
499 darkred: [139, 0, 0],
500 darksalmon: [233, 150, 122],
501 darkseagreen: [143, 188, 143],
502 darkslateblue: [72, 61, 139],
503 darkslategray: [47, 79, 79],
504 darkslategrey: [47, 79, 79],
505 darkturquoise: [0, 206, 209],
506 darkviolet: [148, 0, 211],
507 deeppink: [255, 20, 147],
508 deepskyblue: [0, 191, 255],
509 dimgray: [105, 105, 105],
510 dimgrey: [105, 105, 105],
511 dodgerblue: [30, 144, 255],
512 firebrick: [178, 34, 34],
513 floralwhite: [255, 250, 240],
514 forestgreen: [34, 139, 34],
515 fuchsia: [255, 0, 255],
516 gainsboro: [220, 220, 220],
517 ghostwhite: [248, 248, 255],
518 gold: [255, 215, 0],
519 goldenrod: [218, 165, 32],
520 gray: [128, 128, 128],
521 grey: [128, 128, 128],
522 green: [0, 128, 0],
523 greenyellow: [173, 255, 47],
524 honeydew: [240, 255, 240],
525 hotpink: [255, 105, 180],
526 indianred: [205, 92, 92],
527 indigo: [75, 0, 130],
528 ivory: [255, 255, 240],
529 khaki: [240, 230, 140],
530 lavender: [230, 230, 250],
531 lavenderblush: [255, 240, 245],
532 lawngreen: [124, 252, 0],
533 lemonchiffon: [255, 250, 205],
534 lightblue: [173, 216, 230],
535 lightcoral: [240, 128, 128],
536 lightcyan: [224, 255, 255],
537 lightgoldenrodyellow: [250, 250, 210],
538 lightgray: [211, 211, 211],
539 lightgreen: [144, 238, 144],
540 lightgrey: [211, 211, 211],
541 lightpink: [255, 182, 193],
542 lightsalmon: [255, 160, 122],
543 lightseagreen: [32, 178, 170],
544 lightskyblue: [135, 206, 250],
545 lightslategray: [119, 136, 153],
546 lightslategrey: [119, 136, 153],
547 lightsteelblue: [176, 196, 222],
548 lightyellow: [255, 255, 224],
549 lime: [0, 255, 0],
550 limegreen: [50, 205, 50],
551 linen: [250, 240, 230],
552 magenta: [255, 0, 255],
553 maroon: [128, 0, 0],
554 mediumaquamarine: [102, 205, 170],
555 mediumblue: [0, 0, 205],
556 mediumorchid: [186, 85, 211],
557 mediumpurple: [147, 112, 219],
558 mediumseagreen: [60, 179, 113],
559 mediumslateblue: [123, 104, 238],
560 mediumspringgreen: [0, 250, 154],
561 mediumturquoise: [72, 209, 204],
562 mediumvioletred: [199, 21, 133],
563 midnightblue: [25, 25, 112],
564 mintcream: [245, 255, 250],
565 mistyrose: [255, 228, 225],
566 moccasin: [255, 228, 181],
567 navajowhite: [255, 222, 173],
568 navy: [0, 0, 128],
569 oldlace: [253, 245, 230],
570 olive: [128, 128, 0],
571 olivedrab: [107, 142, 35],
572 orange: [255, 165, 0],
573 orangered: [255, 69, 0],
574 orchid: [218, 112, 214],
575 palegoldenrod: [238, 232, 170],
576 palegreen: [152, 251, 152],
577 paleturquoise: [175, 238, 238],
578 palevioletred: [219, 112, 147],
579 papayawhip: [255, 239, 213],
580 peachpuff: [255, 218, 185],
581 peru: [205, 133, 63],
582 pink: [255, 192, 203],
583 plum: [221, 160, 221],
584 powderblue: [176, 224, 230],
585 purple: [128, 0, 128],
586 red: [255, 0, 0],
587 rosybrown: [188, 143, 143],
588 royalblue: [65, 105, 225],
589 saddlebrown: [139, 69, 19],
590 salmon: [250, 128, 114],
591 sandybrown: [244, 164, 96],
592 seagreen: [46, 139, 87],
593 seashell: [255, 245, 238],
594 sienna: [160, 82, 45],
595 silver: [192, 192, 192],
596 skyblue: [135, 206, 235],
597 slateblue: [106, 90, 205],
598 slategray: [112, 128, 144],
599 slategrey: [112, 128, 144],
600 snow: [255, 250, 250],
601 springgreen: [0, 255, 127],
602 steelblue: [70, 130, 180],
603 tan: [210, 180, 140],
604 teal: [0, 128, 128],
605 thistle: [216, 191, 216],
606 tomato: [255, 99, 71],
607 turquoise: [64, 224, 208],
608 violet: [238, 130, 238],
609 wheat: [245, 222, 179],
610 white: [255, 255, 255],
611 whitesmoke: [245, 245, 245],
612 yellow: [255, 255, 0],
613 yellowgreen: [154, 205, 50]
614};
615
616var setMap = function setMap(options) {
617 var obj = options.map;
618 var keys = options.keys;
619 var l = keys.length;
620
621 for (var i = 0; i < l; i++) {
622 var key = keys[i];
623
624 if (plainObject(key)) {
625 throw Error('Tried to set map with object key');
626 }
627
628 if (i < keys.length - 1) {
629 // extend the map if necessary
630 if (obj[key] == null) {
631 obj[key] = {};
632 }
633
634 obj = obj[key];
635 } else {
636 // set the value
637 obj[key] = options.value;
638 }
639 }
640}; // gets the value in a map even if it's not built in places
641
642var getMap = function getMap(options) {
643 var obj = options.map;
644 var keys = options.keys;
645 var l = keys.length;
646
647 for (var i = 0; i < l; i++) {
648 var key = keys[i];
649
650 if (plainObject(key)) {
651 throw Error('Tried to get map with object key');
652 }
653
654 obj = obj[key];
655
656 if (obj == null) {
657 return obj;
658 }
659 }
660
661 return obj;
662}; // deletes the entry in the map
663
664var performance = window$1 ? window$1.performance : null;
665var pnow = performance && performance.now ? function () {
666 return performance.now();
667} : function () {
668 return Date.now();
669};
670
671var raf = function () {
672 if (window$1) {
673 if (window$1.requestAnimationFrame) {
674 return function (fn) {
675 window$1.requestAnimationFrame(fn);
676 };
677 } else if (window$1.mozRequestAnimationFrame) {
678 return function (fn) {
679 window$1.mozRequestAnimationFrame(fn);
680 };
681 } else if (window$1.webkitRequestAnimationFrame) {
682 return function (fn) {
683 window$1.webkitRequestAnimationFrame(fn);
684 };
685 } else if (window$1.msRequestAnimationFrame) {
686 return function (fn) {
687 window$1.msRequestAnimationFrame(fn);
688 };
689 }
690 }
691
692 return function (fn) {
693 if (fn) {
694 setTimeout(function () {
695 fn(pnow());
696 }, 1000 / 60);
697 }
698 };
699}();
700
701var requestAnimationFrame = function requestAnimationFrame(fn) {
702 return raf(fn);
703};
704var performanceNow = pnow;
705
706var DEFAULT_HASH_SEED = 9261;
707var K = 65599; // 37 also works pretty well
708
709var DEFAULT_HASH_SEED_ALT = 5381;
710var hashIterableInts = function hashIterableInts(iterator) {
711 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
712 // sdbm/string-hash
713 var hash = seed;
714 var entry;
715
716 for (;;) {
717 entry = iterator.next();
718
719 if (entry.done) {
720 break;
721 }
722
723 hash = hash * K + entry.value | 0;
724 }
725
726 return hash;
727};
728var hashInt = function hashInt(num) {
729 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
730 // sdbm/string-hash
731 return seed * K + num | 0;
732};
733var hashIntAlt = function hashIntAlt(num) {
734 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
735 // djb2/string-hash
736 return (seed << 5) + seed + num | 0;
737};
738var combineHashes = function combineHashes(hash1, hash2) {
739 return hash1 * 0x200000 + hash2;
740};
741var combineHashesArray = function combineHashesArray(hashes) {
742 return hashes[0] * 0x200000 + hashes[1];
743};
744var hashArrays = function hashArrays(hashes1, hashes2) {
745 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
746};
747var hashIntsArray = function hashIntsArray(ints, seed) {
748 var entry = {
749 value: 0,
750 done: false
751 };
752 var i = 0;
753 var length = ints.length;
754 var iterator = {
755 next: function next() {
756 if (i < length) {
757 entry.value = ints[i++];
758 } else {
759 entry.done = true;
760 }
761
762 return entry;
763 }
764 };
765 return hashIterableInts(iterator, seed);
766};
767var hashString = function hashString(str, seed) {
768 var entry = {
769 value: 0,
770 done: false
771 };
772 var i = 0;
773 var length = str.length;
774 var iterator = {
775 next: function next() {
776 if (i < length) {
777 entry.value = str.charCodeAt(i++);
778 } else {
779 entry.done = true;
780 }
781
782 return entry;
783 }
784 };
785 return hashIterableInts(iterator, seed);
786};
787var hashStrings = function hashStrings() {
788 return hashStringsArray(arguments);
789};
790var hashStringsArray = function hashStringsArray(strs) {
791 var hash;
792
793 for (var i = 0; i < strs.length; i++) {
794 var str = strs[i];
795
796 if (i === 0) {
797 hash = hashString(str);
798 } else {
799 hash = hashString(str, hash);
800 }
801 }
802
803 return hash;
804};
805
806/*global console */
807var warningsEnabled = true;
808var warnSupported = console.warn != null; // eslint-disable-line no-console
809
810var traceSupported = console.trace != null; // eslint-disable-line no-console
811
812var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
813var trueify = function trueify() {
814 return true;
815};
816var falsify = function falsify() {
817 return false;
818};
819var zeroify = function zeroify() {
820 return 0;
821};
822var noop = function noop() {};
823var error = function error(msg) {
824 throw new Error(msg);
825};
826var warnings = function warnings(enabled) {
827 if (enabled !== undefined) {
828 warningsEnabled = !!enabled;
829 } else {
830 return warningsEnabled;
831 }
832};
833var warn = function warn(msg) {
834 /* eslint-disable no-console */
835 if (!warnings()) {
836 return;
837 }
838
839 if (warnSupported) {
840 console.warn(msg);
841 } else {
842 console.log(msg);
843
844 if (traceSupported) {
845 console.trace();
846 }
847 }
848};
849/* eslint-enable */
850
851var clone = function clone(obj) {
852 return extend({}, obj);
853}; // gets a shallow copy of the argument
854
855var copy = function copy(obj) {
856 if (obj == null) {
857 return obj;
858 }
859
860 if (array(obj)) {
861 return obj.slice();
862 } else if (plainObject(obj)) {
863 return clone(obj);
864 } else {
865 return obj;
866 }
867};
868var copyArray = function copyArray(arr) {
869 return arr.slice();
870};
871var uuid = function uuid(a, b
872/* placeholders */
873) {
874 for ( // loop :)
875 b = a = ''; // b - result , a - numeric letiable
876 a++ < 36; //
877 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
878 ? // return a random number or 4
879 (a ^ 15 // if "a" is not 15
880 ? // genetate a random number from 0 to 15
881 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
882 : 4 // otherwise 4
883 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
884 ) {
885 }
886
887 return b;
888};
889var _staticEmptyObject = {};
890var staticEmptyObject = function staticEmptyObject() {
891 return _staticEmptyObject;
892};
893var defaults = function defaults(_defaults) {
894 var keys = Object.keys(_defaults);
895 return function (opts) {
896 var filledOpts = {};
897
898 for (var i = 0; i < keys.length; i++) {
899 var key = keys[i];
900 var optVal = opts == null ? undefined : opts[key];
901 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
902 }
903
904 return filledOpts;
905 };
906};
907var removeFromArray = function removeFromArray(arr, ele, oneCopy) {
908 for (var i = arr.length - 1; i >= 0; i--) {
909 if (arr[i] === ele) {
910 arr.splice(i, 1);
911
912 if (oneCopy) {
913 break;
914 }
915 }
916 }
917};
918var clearArray = function clearArray(arr) {
919 arr.splice(0, arr.length);
920};
921var push = function push(arr, otherArr) {
922 for (var i = 0; i < otherArr.length; i++) {
923 var el = otherArr[i];
924 arr.push(el);
925 }
926};
927var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
928 if (prefix) {
929 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
930 }
931
932 return obj[propName];
933};
934var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
935 if (prefix) {
936 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
937 }
938
939 obj[propName] = value;
940};
941
942/* global Map */
943var ObjectMap =
944/*#__PURE__*/
945function () {
946 function ObjectMap() {
947 _classCallCheck(this, ObjectMap);
948
949 this._obj = {};
950 }
951
952 _createClass(ObjectMap, [{
953 key: "set",
954 value: function set(key, val) {
955 this._obj[key] = val;
956 return this;
957 }
958 }, {
959 key: "delete",
960 value: function _delete(key) {
961 this._obj[key] = undefined;
962 return this;
963 }
964 }, {
965 key: "clear",
966 value: function clear() {
967 this._obj = {};
968 }
969 }, {
970 key: "has",
971 value: function has(key) {
972 return this._obj[key] !== undefined;
973 }
974 }, {
975 key: "get",
976 value: function get(key) {
977 return this._obj[key];
978 }
979 }]);
980
981 return ObjectMap;
982}();
983
984var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
985
986/* global Set */
987var undef = "undefined" ;
988
989var ObjectSet =
990/*#__PURE__*/
991function () {
992 function ObjectSet(arrayOrObjectSet) {
993 _classCallCheck(this, ObjectSet);
994
995 this._obj = Object.create(null);
996 this.size = 0;
997
998 if (arrayOrObjectSet != null) {
999 var arr;
1000
1001 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1002 arr = arrayOrObjectSet.toArray();
1003 } else {
1004 arr = arrayOrObjectSet;
1005 }
1006
1007 for (var i = 0; i < arr.length; i++) {
1008 this.add(arr[i]);
1009 }
1010 }
1011 }
1012
1013 _createClass(ObjectSet, [{
1014 key: "instanceString",
1015 value: function instanceString() {
1016 return 'set';
1017 }
1018 }, {
1019 key: "add",
1020 value: function add(val) {
1021 var o = this._obj;
1022
1023 if (o[val] !== 1) {
1024 o[val] = 1;
1025 this.size++;
1026 }
1027 }
1028 }, {
1029 key: "delete",
1030 value: function _delete(val) {
1031 var o = this._obj;
1032
1033 if (o[val] === 1) {
1034 o[val] = 0;
1035 this.size--;
1036 }
1037 }
1038 }, {
1039 key: "clear",
1040 value: function clear() {
1041 this._obj = Object.create(null);
1042 }
1043 }, {
1044 key: "has",
1045 value: function has(val) {
1046 return this._obj[val] === 1;
1047 }
1048 }, {
1049 key: "toArray",
1050 value: function toArray() {
1051 var _this = this;
1052
1053 return Object.keys(this._obj).filter(function (key) {
1054 return _this.has(key);
1055 });
1056 }
1057 }, {
1058 key: "forEach",
1059 value: function forEach(callback, thisArg) {
1060 return this.toArray().forEach(callback, thisArg);
1061 }
1062 }]);
1063
1064 return ObjectSet;
1065}();
1066
1067var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1068
1069var Element = function Element(cy, params) {
1070 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1071
1072 if (cy === undefined || params === undefined || !core(cy)) {
1073 error('An element must have a core reference and parameters set');
1074 return;
1075 }
1076
1077 var group = params.group; // try to automatically infer the group if unspecified
1078
1079 if (group == null) {
1080 if (params.data && params.data.source != null && params.data.target != null) {
1081 group = 'edges';
1082 } else {
1083 group = 'nodes';
1084 }
1085 } // validate group
1086
1087
1088 if (group !== 'nodes' && group !== 'edges') {
1089 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1090 return;
1091 } // make the element array-like, just like a collection
1092
1093
1094 this.length = 1;
1095 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1096
1097 var _p = this._private = {
1098 cy: cy,
1099 single: true,
1100 // indicates this is an element
1101 data: params.data || {},
1102 // data object
1103 position: params.position || {
1104 x: 0,
1105 y: 0
1106 },
1107 // (x, y) position pair
1108 autoWidth: undefined,
1109 // width and height of nodes calculated by the renderer when set to special 'auto' value
1110 autoHeight: undefined,
1111 autoPadding: undefined,
1112 compoundBoundsClean: false,
1113 // whether the compound dimensions need to be recalculated the next time dimensions are read
1114 listeners: [],
1115 // array of bound listeners
1116 group: group,
1117 // string; 'nodes' or 'edges'
1118 style: {},
1119 // properties as set by the style
1120 rstyle: {},
1121 // properties for style sent from the renderer to the core
1122 styleCxts: [],
1123 // applied style contexts from the styler
1124 styleKeys: {},
1125 // per-group keys of style property values
1126 removed: true,
1127 // whether it's inside the vis; true if removed (set true here since we call restore)
1128 selected: params.selected ? true : false,
1129 // whether it's selected
1130 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1131 // whether it's selectable
1132 locked: params.locked ? true : false,
1133 // whether the element is locked (cannot be moved)
1134 grabbed: false,
1135 // whether the element is grabbed by the mouse; renderer sets this privately
1136 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1137 // whether the element can be grabbed
1138 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1139 // whether the element has passthrough panning enabled
1140 active: false,
1141 // whether the element is active from user interaction
1142 classes: new Set$1(),
1143 // map ( className => true )
1144 animation: {
1145 // object for currently-running animations
1146 current: [],
1147 queue: []
1148 },
1149 rscratch: {},
1150 // object in which the renderer can store information
1151 scratch: params.scratch || {},
1152 // scratch objects
1153 edges: [],
1154 // array of connected edges
1155 children: [],
1156 // array of children
1157 parent: null,
1158 // parent ref
1159 traversalCache: {},
1160 // cache of output of traversal functions
1161 backgrounding: false,
1162 // whether background images are loading
1163 bbCache: null,
1164 // cache of the current bounding box
1165 bbCacheShift: {
1166 x: 0,
1167 y: 0
1168 },
1169 // shift applied to cached bb to be applied on next get
1170 bodyBounds: null,
1171 // bounds cache of element body, w/o overlay
1172 overlayBounds: null,
1173 // bounds cache of element body, including overlay
1174 labelBounds: {
1175 // bounds cache of labels
1176 all: null,
1177 source: null,
1178 target: null,
1179 main: null
1180 },
1181 arrowBounds: {
1182 // bounds cache of edge arrows
1183 source: null,
1184 target: null,
1185 'mid-source': null,
1186 'mid-target': null
1187 }
1188 };
1189
1190 if (_p.position.x == null) {
1191 _p.position.x = 0;
1192 }
1193
1194 if (_p.position.y == null) {
1195 _p.position.y = 0;
1196 } // renderedPosition overrides if specified
1197
1198
1199 if (params.renderedPosition) {
1200 var rpos = params.renderedPosition;
1201 var pan = cy.pan();
1202 var zoom = cy.zoom();
1203 _p.position = {
1204 x: (rpos.x - pan.x) / zoom,
1205 y: (rpos.y - pan.y) / zoom
1206 };
1207 }
1208
1209 var classes = [];
1210
1211 if (array(params.classes)) {
1212 classes = params.classes;
1213 } else if (string(params.classes)) {
1214 classes = params.classes.split(/\s+/);
1215 }
1216
1217 for (var i = 0, l = classes.length; i < l; i++) {
1218 var cls = classes[i];
1219
1220 if (!cls || cls === '') {
1221 continue;
1222 }
1223
1224 _p.classes.add(cls);
1225 }
1226
1227 this.createEmitter();
1228 var bypass = params.style || params.css;
1229
1230 if (bypass) {
1231 warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
1232 this.style(bypass);
1233 }
1234
1235 if (restore === undefined || restore) {
1236 this.restore();
1237 }
1238};
1239
1240var defineSearch = function defineSearch(params) {
1241 params = {
1242 bfs: params.bfs || !params.dfs,
1243 dfs: params.dfs || !params.bfs
1244 }; // from pseudocode on wikipedia
1245
1246 return function searchFn(roots, fn$1, directed) {
1247 var options;
1248
1249 if (plainObject(roots) && !elementOrCollection(roots)) {
1250 options = roots;
1251 roots = options.roots || options.root;
1252 fn$1 = options.visit;
1253 directed = options.directed;
1254 }
1255
1256 directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
1257 fn$1 = fn(fn$1) ? fn$1 : function () {};
1258 var cy = this._private.cy;
1259 var v = roots = string(roots) ? this.filter(roots) : roots;
1260 var Q = [];
1261 var connectedNodes = [];
1262 var connectedBy = {};
1263 var id2depth = {};
1264 var V = {};
1265 var j = 0;
1266 var found;
1267
1268 var _this$byGroup = this.byGroup(),
1269 nodes = _this$byGroup.nodes,
1270 edges = _this$byGroup.edges; // enqueue v
1271
1272
1273 for (var i = 0; i < v.length; i++) {
1274 var vi = v[i];
1275 var viId = vi.id();
1276
1277 if (vi.isNode()) {
1278 Q.unshift(vi);
1279
1280 if (params.bfs) {
1281 V[viId] = true;
1282 connectedNodes.push(vi);
1283 }
1284
1285 id2depth[viId] = 0;
1286 }
1287 }
1288
1289 var _loop2 = function _loop2() {
1290 var v = params.bfs ? Q.shift() : Q.pop();
1291 var vId = v.id();
1292
1293 if (params.dfs) {
1294 if (V[vId]) {
1295 return "continue";
1296 }
1297
1298 V[vId] = true;
1299 connectedNodes.push(v);
1300 }
1301
1302 var depth = id2depth[vId];
1303 var prevEdge = connectedBy[vId];
1304 var src = prevEdge != null ? prevEdge.source() : null;
1305 var tgt = prevEdge != null ? prevEdge.target() : null;
1306 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1307 var ret = void 0;
1308 ret = fn$1(v, prevEdge, prevNode, j++, depth);
1309
1310 if (ret === true) {
1311 found = v;
1312 return "break";
1313 }
1314
1315 if (ret === false) {
1316 return "break";
1317 }
1318
1319 var vwEdges = v.connectedEdges().filter(function (e) {
1320 return (!directed || e.source().same(v)) && edges.has(e);
1321 });
1322
1323 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1324 var e = vwEdges[_i2];
1325 var w = e.connectedNodes().filter(function (n) {
1326 return !n.same(v) && nodes.has(n);
1327 });
1328 var wId = w.id();
1329
1330 if (w.length !== 0 && !V[wId]) {
1331 w = w[0];
1332 Q.push(w);
1333
1334 if (params.bfs) {
1335 V[wId] = true;
1336 connectedNodes.push(w);
1337 }
1338
1339 connectedBy[wId] = e;
1340 id2depth[wId] = id2depth[vId] + 1;
1341 }
1342 }
1343 };
1344
1345 _loop: while (Q.length !== 0) {
1346 var _ret = _loop2();
1347
1348 switch (_ret) {
1349 case "continue":
1350 continue;
1351
1352 case "break":
1353 break _loop;
1354 }
1355 }
1356
1357 var connectedEles = cy.collection();
1358
1359 for (var _i = 0; _i < connectedNodes.length; _i++) {
1360 var node = connectedNodes[_i];
1361 var edge = connectedBy[node.id()];
1362
1363 if (edge != null) {
1364 connectedEles.push(edge);
1365 }
1366
1367 connectedEles.push(node);
1368 }
1369
1370 return {
1371 path: cy.collection(connectedEles),
1372 found: cy.collection(found)
1373 };
1374 };
1375}; // search, spanning trees, etc
1376
1377
1378var elesfn = {
1379 breadthFirstSearch: defineSearch({
1380 bfs: true
1381 }),
1382 depthFirstSearch: defineSearch({
1383 dfs: true
1384 })
1385}; // nice, short mathemathical alias
1386
1387elesfn.bfs = elesfn.breadthFirstSearch;
1388elesfn.dfs = elesfn.depthFirstSearch;
1389
1390var dijkstraDefaults = defaults({
1391 root: null,
1392 weight: function weight(edge) {
1393 return 1;
1394 },
1395 directed: false
1396});
1397var elesfn$1 = {
1398 dijkstra: function dijkstra(options) {
1399 if (!plainObject(options)) {
1400 var args = arguments;
1401 options = {
1402 root: args[0],
1403 weight: args[1],
1404 directed: args[2]
1405 };
1406 }
1407
1408 var _dijkstraDefaults = dijkstraDefaults(options),
1409 root = _dijkstraDefaults.root,
1410 weight = _dijkstraDefaults.weight,
1411 directed = _dijkstraDefaults.directed;
1412
1413 var eles = this;
1414 var weightFn = weight;
1415 var source = string(root) ? this.filter(root)[0] : root[0];
1416 var dist = {};
1417 var prev = {};
1418 var knownDist = {};
1419
1420 var _this$byGroup = this.byGroup(),
1421 nodes = _this$byGroup.nodes,
1422 edges = _this$byGroup.edges;
1423
1424 edges.unmergeBy(function (ele) {
1425 return ele.isLoop();
1426 });
1427
1428 var getDist = function getDist(node) {
1429 return dist[node.id()];
1430 };
1431
1432 var setDist = function setDist(node, d) {
1433 dist[node.id()] = d;
1434 Q.updateItem(node);
1435 };
1436
1437 var Q = new Heap(function (a, b) {
1438 return getDist(a) - getDist(b);
1439 });
1440
1441 for (var i = 0; i < nodes.length; i++) {
1442 var node = nodes[i];
1443 dist[node.id()] = node.same(source) ? 0 : Infinity;
1444 Q.push(node);
1445 }
1446
1447 var distBetween = function distBetween(u, v) {
1448 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
1449 var smallestDistance = Infinity;
1450 var smallestEdge;
1451
1452 for (var _i = 0; _i < uvs.length; _i++) {
1453 var edge = uvs[_i];
1454
1455 var _weight = weightFn(edge);
1456
1457 if (_weight < smallestDistance || !smallestEdge) {
1458 smallestDistance = _weight;
1459 smallestEdge = edge;
1460 }
1461 }
1462
1463 return {
1464 edge: smallestEdge,
1465 dist: smallestDistance
1466 };
1467 };
1468
1469 while (Q.size() > 0) {
1470 var u = Q.pop();
1471 var smalletsDist = getDist(u);
1472 var uid = u.id();
1473 knownDist[uid] = smalletsDist;
1474
1475 if (smalletsDist === Infinity) {
1476 continue;
1477 }
1478
1479 var neighbors = u.neighborhood().intersect(nodes);
1480
1481 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
1482 var v = neighbors[_i2];
1483 var vid = v.id();
1484 var vDist = distBetween(u, v);
1485 var alt = smalletsDist + vDist.dist;
1486
1487 if (alt < getDist(v)) {
1488 setDist(v, alt);
1489 prev[vid] = {
1490 node: u,
1491 edge: vDist.edge
1492 };
1493 }
1494 } // for
1495
1496 } // while
1497
1498
1499 return {
1500 distanceTo: function distanceTo(node) {
1501 var target = string(node) ? nodes.filter(node)[0] : node[0];
1502 return knownDist[target.id()];
1503 },
1504 pathTo: function pathTo(node) {
1505 var target = string(node) ? nodes.filter(node)[0] : node[0];
1506 var S = [];
1507 var u = target;
1508 var uid = u.id();
1509
1510 if (target.length > 0) {
1511 S.unshift(target);
1512
1513 while (prev[uid]) {
1514 var p = prev[uid];
1515 S.unshift(p.edge);
1516 S.unshift(p.node);
1517 u = p.node;
1518 uid = u.id();
1519 }
1520 }
1521
1522 return eles.spawn(S);
1523 }
1524 };
1525 }
1526};
1527
1528var elesfn$2 = {
1529 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
1530 // implemented from pseudocode from wikipedia
1531 kruskal: function kruskal(weightFn) {
1532 weightFn = weightFn || function (edge) {
1533 return 1;
1534 };
1535
1536 var _this$byGroup = this.byGroup(),
1537 nodes = _this$byGroup.nodes,
1538 edges = _this$byGroup.edges;
1539
1540 var numNodes = nodes.length;
1541 var forest = new Array(numNodes);
1542 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
1543
1544 var findSetIndex = function findSetIndex(ele) {
1545 for (var i = 0; i < forest.length; i++) {
1546 var eles = forest[i];
1547
1548 if (eles.has(ele)) {
1549 return i;
1550 }
1551 }
1552 }; // start with one forest per node
1553
1554
1555 for (var i = 0; i < numNodes; i++) {
1556 forest[i] = this.spawn(nodes[i]);
1557 }
1558
1559 var S = edges.sort(function (a, b) {
1560 return weightFn(a) - weightFn(b);
1561 });
1562
1563 for (var _i = 0; _i < S.length; _i++) {
1564 var edge = S[_i];
1565 var u = edge.source()[0];
1566 var v = edge.target()[0];
1567 var setUIndex = findSetIndex(u);
1568 var setVIndex = findSetIndex(v);
1569 var setU = forest[setUIndex];
1570 var setV = forest[setVIndex];
1571
1572 if (setUIndex !== setVIndex) {
1573 A.merge(edge); // combine forests for u and v
1574
1575 setU.merge(setV);
1576 forest.splice(setVIndex, 1);
1577 }
1578 }
1579
1580 return A;
1581 }
1582};
1583
1584var aStarDefaults = defaults({
1585 root: null,
1586 goal: null,
1587 weight: function weight(edge) {
1588 return 1;
1589 },
1590 heuristic: function heuristic(edge) {
1591 return 0;
1592 },
1593 directed: false
1594});
1595var elesfn$3 = {
1596 // Implemented from pseudocode from wikipedia
1597 aStar: function aStar(options) {
1598 var cy = this.cy();
1599
1600 var _aStarDefaults = aStarDefaults(options),
1601 root = _aStarDefaults.root,
1602 goal = _aStarDefaults.goal,
1603 heuristic = _aStarDefaults.heuristic,
1604 directed = _aStarDefaults.directed,
1605 weight = _aStarDefaults.weight;
1606
1607 root = cy.collection(root)[0];
1608 goal = cy.collection(goal)[0];
1609 var sid = root.id();
1610 var tid = goal.id();
1611 var gScore = {};
1612 var fScore = {};
1613 var closedSetIds = {};
1614 var openSet = new Heap(function (a, b) {
1615 return fScore[a.id()] - fScore[b.id()];
1616 });
1617 var openSetIds = new Set$1();
1618 var cameFrom = {};
1619 var cameFromEdge = {};
1620
1621 var addToOpenSet = function addToOpenSet(ele, id) {
1622 openSet.push(ele);
1623 openSetIds.add(id);
1624 };
1625
1626 var cMin, cMinId;
1627
1628 var popFromOpenSet = function popFromOpenSet() {
1629 cMin = openSet.pop();
1630 cMinId = cMin.id();
1631 openSetIds["delete"](cMinId);
1632 };
1633
1634 var isInOpenSet = function isInOpenSet(id) {
1635 return openSetIds.has(id);
1636 };
1637
1638 addToOpenSet(root, sid);
1639 gScore[sid] = 0;
1640 fScore[sid] = heuristic(root); // Counter
1641
1642 var steps = 0; // Main loop
1643
1644 while (openSet.size() > 0) {
1645 popFromOpenSet();
1646 steps++; // If we've found our goal, then we are done
1647
1648 if (cMinId === tid) {
1649 var path = [];
1650 var pathNode = goal;
1651 var pathNodeId = tid;
1652 var pathEdge = cameFromEdge[pathNodeId];
1653
1654 for (;;) {
1655 path.unshift(pathNode);
1656
1657 if (pathEdge != null) {
1658 path.unshift(pathEdge);
1659 }
1660
1661 pathNode = cameFrom[pathNodeId];
1662
1663 if (pathNode == null) {
1664 break;
1665 }
1666
1667 pathNodeId = pathNode.id();
1668 pathEdge = cameFromEdge[pathNodeId];
1669 }
1670
1671 return {
1672 found: true,
1673 distance: gScore[cMinId],
1674 path: this.spawn(path),
1675 steps: steps
1676 };
1677 } // Add cMin to processed nodes
1678
1679
1680 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
1681 // Take into account if graph is directed or not
1682
1683 var vwEdges = cMin._private.edges;
1684
1685 for (var i = 0; i < vwEdges.length; i++) {
1686 var e = vwEdges[i]; // edge must be in set of calling eles
1687
1688 if (!this.hasElementWithId(e.id())) {
1689 continue;
1690 } // cMin must be the source of edge if directed
1691
1692
1693 if (directed && e.data('source') !== cMinId) {
1694 continue;
1695 }
1696
1697 var wSrc = e.source();
1698 var wTgt = e.target();
1699 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
1700 var wid = w.id(); // node must be in set of calling eles
1701
1702 if (!this.hasElementWithId(wid)) {
1703 continue;
1704 } // if node is in closedSet, ignore it
1705
1706
1707 if (closedSetIds[wid]) {
1708 continue;
1709 } // New tentative score for node w
1710
1711
1712 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
1713 // w not present in openSet
1714 // OR
1715 // tentative gScore is less than previous value
1716 // w not in openSet
1717
1718 if (!isInOpenSet(wid)) {
1719 gScore[wid] = tempScore;
1720 fScore[wid] = tempScore + heuristic(w);
1721 addToOpenSet(w, wid);
1722 cameFrom[wid] = cMin;
1723 cameFromEdge[wid] = e;
1724 continue;
1725 } // w already in openSet, but with greater gScore
1726
1727
1728 if (tempScore < gScore[wid]) {
1729 gScore[wid] = tempScore;
1730 fScore[wid] = tempScore + heuristic(w);
1731 cameFrom[wid] = cMin;
1732 cameFromEdge[wid] = e;
1733 }
1734 } // End of neighbors update
1735
1736 } // End of main loop
1737 // If we've reached here, then we've not reached our goal
1738
1739
1740 return {
1741 found: false,
1742 distance: undefined,
1743 path: undefined,
1744 steps: steps
1745 };
1746 }
1747}; // elesfn
1748
1749var floydWarshallDefaults = defaults({
1750 weight: function weight(edge) {
1751 return 1;
1752 },
1753 directed: false
1754});
1755var elesfn$4 = {
1756 // Implemented from pseudocode from wikipedia
1757 floydWarshall: function floydWarshall(options) {
1758 var cy = this.cy();
1759
1760 var _floydWarshallDefault = floydWarshallDefaults(options),
1761 weight = _floydWarshallDefault.weight,
1762 directed = _floydWarshallDefault.directed;
1763
1764 var weightFn = weight;
1765
1766 var _this$byGroup = this.byGroup(),
1767 nodes = _this$byGroup.nodes,
1768 edges = _this$byGroup.edges;
1769
1770 var N = nodes.length;
1771 var Nsq = N * N;
1772
1773 var indexOf = function indexOf(node) {
1774 return nodes.indexOf(node);
1775 };
1776
1777 var atIndex = function atIndex(i) {
1778 return nodes[i];
1779 }; // Initialize distance matrix
1780
1781
1782 var dist = new Array(Nsq);
1783
1784 for (var n = 0; n < Nsq; n++) {
1785 var j = n % N;
1786 var i = (n - j) / N;
1787
1788 if (i === j) {
1789 dist[n] = 0;
1790 } else {
1791 dist[n] = Infinity;
1792 }
1793 } // Initialize matrix used for path reconstruction
1794 // Initialize distance matrix
1795
1796
1797 var next = new Array(Nsq);
1798 var edgeNext = new Array(Nsq); // Process edges
1799
1800 for (var _i = 0; _i < edges.length; _i++) {
1801 var edge = edges[_i];
1802 var src = edge.source()[0];
1803 var tgt = edge.target()[0];
1804
1805 if (src === tgt) {
1806 continue;
1807 } // exclude loops
1808
1809
1810 var s = indexOf(src);
1811 var t = indexOf(tgt);
1812 var st = s * N + t; // source to target index
1813
1814 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
1815
1816
1817 if (dist[st] > _weight) {
1818 dist[st] = _weight;
1819 next[st] = t;
1820 edgeNext[st] = edge;
1821 } // If undirected graph, process 'reversed' edge
1822
1823
1824 if (!directed) {
1825 var ts = t * N + s; // target to source index
1826
1827 if (!directed && dist[ts] > _weight) {
1828 dist[ts] = _weight;
1829 next[ts] = s;
1830 edgeNext[ts] = edge;
1831 }
1832 }
1833 } // Main loop
1834
1835
1836 for (var k = 0; k < N; k++) {
1837 for (var _i2 = 0; _i2 < N; _i2++) {
1838 var ik = _i2 * N + k;
1839
1840 for (var _j = 0; _j < N; _j++) {
1841 var ij = _i2 * N + _j;
1842 var kj = k * N + _j;
1843
1844 if (dist[ik] + dist[kj] < dist[ij]) {
1845 dist[ij] = dist[ik] + dist[kj];
1846 next[ij] = next[ik];
1847 }
1848 }
1849 }
1850 }
1851
1852 var getArgEle = function getArgEle(ele) {
1853 return (string(ele) ? cy.filter(ele) : ele)[0];
1854 };
1855
1856 var indexOfArgEle = function indexOfArgEle(ele) {
1857 return indexOf(getArgEle(ele));
1858 };
1859
1860 var res = {
1861 distance: function distance(from, to) {
1862 var i = indexOfArgEle(from);
1863 var j = indexOfArgEle(to);
1864 return dist[i * N + j];
1865 },
1866 path: function path(from, to) {
1867 var i = indexOfArgEle(from);
1868 var j = indexOfArgEle(to);
1869 var fromNode = atIndex(i);
1870
1871 if (i === j) {
1872 return fromNode.collection();
1873 }
1874
1875 if (next[i * N + j] == null) {
1876 return cy.collection();
1877 }
1878
1879 var path = cy.collection();
1880 var prev = i;
1881 var edge;
1882 path.merge(fromNode);
1883
1884 while (i !== j) {
1885 prev = i;
1886 i = next[i * N + j];
1887 edge = edgeNext[prev * N + i];
1888 path.merge(edge);
1889 path.merge(atIndex(i));
1890 }
1891
1892 return path;
1893 }
1894 };
1895 return res;
1896 } // floydWarshall
1897
1898}; // elesfn
1899
1900var bellmanFordDefaults = defaults({
1901 weight: function weight(edge) {
1902 return 1;
1903 },
1904 directed: false,
1905 root: null
1906});
1907var elesfn$5 = {
1908 // Implemented from pseudocode from wikipedia
1909 bellmanFord: function bellmanFord(options) {
1910 var _this = this;
1911
1912 var _bellmanFordDefaults = bellmanFordDefaults(options),
1913 weight = _bellmanFordDefaults.weight,
1914 directed = _bellmanFordDefaults.directed,
1915 root = _bellmanFordDefaults.root;
1916
1917 var weightFn = weight;
1918 var eles = this;
1919 var cy = this.cy();
1920
1921 var _this$byGroup = this.byGroup(),
1922 edges = _this$byGroup.edges,
1923 nodes = _this$byGroup.nodes;
1924
1925 var numNodes = nodes.length;
1926 var infoMap = new Map$1();
1927 var hasNegativeWeightCycle = false;
1928 var negativeWeightCycles = [];
1929 root = cy.collection(root)[0]; // in case selector passed
1930
1931 edges.unmergeBy(function (edge) {
1932 return edge.isLoop();
1933 });
1934 var numEdges = edges.length;
1935
1936 var getInfo = function getInfo(node) {
1937 var obj = infoMap.get(node.id());
1938
1939 if (!obj) {
1940 obj = {};
1941 infoMap.set(node.id(), obj);
1942 }
1943
1944 return obj;
1945 };
1946
1947 var getNodeFromTo = function getNodeFromTo(to) {
1948 return (string(to) ? cy.$(to) : to)[0];
1949 };
1950
1951 var distanceTo = function distanceTo(to) {
1952 return getInfo(getNodeFromTo(to)).dist;
1953 };
1954
1955 var pathTo = function pathTo(to) {
1956 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
1957 var end = getNodeFromTo(to);
1958 var path = [];
1959 var node = end;
1960
1961 for (;;) {
1962 if (node == null) {
1963 return _this.spawn();
1964 }
1965
1966 var _getInfo = getInfo(node),
1967 edge = _getInfo.edge,
1968 pred = _getInfo.pred;
1969
1970 path.unshift(node[0]);
1971
1972 if (node.same(thisStart) && path.length > 0) {
1973 break;
1974 }
1975
1976 if (edge != null) {
1977 path.unshift(edge);
1978 }
1979
1980 node = pred;
1981 }
1982
1983 return eles.spawn(path);
1984 }; // Initializations { dist, pred, edge }
1985
1986
1987 for (var i = 0; i < numNodes; i++) {
1988 var node = nodes[i];
1989 var info = getInfo(node);
1990
1991 if (node.same(root)) {
1992 info.dist = 0;
1993 } else {
1994 info.dist = Infinity;
1995 }
1996
1997 info.pred = null;
1998 info.edge = null;
1999 } // Edges relaxation
2000
2001
2002 var replacedEdge = false;
2003
2004 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2005 var dist = info1.dist + weight;
2006
2007 if (dist < info2.dist && !edge.same(info1.edge)) {
2008 info2.dist = dist;
2009 info2.pred = node1;
2010 info2.edge = edge;
2011 replacedEdge = true;
2012 }
2013 };
2014
2015 for (var _i = 1; _i < numNodes; _i++) {
2016 replacedEdge = false;
2017
2018 for (var e = 0; e < numEdges; e++) {
2019 var edge = edges[e];
2020 var src = edge.source();
2021 var tgt = edge.target();
2022
2023 var _weight = weightFn(edge);
2024
2025 var srcInfo = getInfo(src);
2026 var tgtInfo = getInfo(tgt);
2027 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2028
2029 if (!directed) {
2030 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2031 }
2032 }
2033
2034 if (!replacedEdge) {
2035 break;
2036 }
2037 }
2038
2039 if (replacedEdge) {
2040 // Check for negative weight cycles
2041 for (var _e = 0; _e < numEdges; _e++) {
2042 var _edge = edges[_e];
2043
2044 var _src = _edge.source();
2045
2046 var _tgt = _edge.target();
2047
2048 var _weight2 = weightFn(_edge);
2049
2050 var srcDist = getInfo(_src).dist;
2051 var tgtDist = getInfo(_tgt).dist;
2052
2053 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2054 warn('Graph contains a negative weight cycle for Bellman-Ford');
2055 hasNegativeWeightCycle = true;
2056 break;
2057 }
2058 }
2059 }
2060
2061 return {
2062 distanceTo: distanceTo,
2063 pathTo: pathTo,
2064 hasNegativeWeightCycle: hasNegativeWeightCycle,
2065 negativeWeightCycles: negativeWeightCycles
2066 };
2067 } // bellmanFord
2068
2069}; // elesfn
2070
2071var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2072// Updates the remaining edge lists
2073// Receives as a paramater the edge which causes the collapse
2074
2075var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2076 if (remainingEdges.length === 0) {
2077 error("Karger-Stein must be run on a connected (sub)graph");
2078 }
2079
2080 var edgeInfo = remainingEdges[edgeIndex];
2081 var sourceIn = edgeInfo[1];
2082 var targetIn = edgeInfo[2];
2083 var partition1 = nodeMap[sourceIn];
2084 var partition2 = nodeMap[targetIn];
2085 var newEdges = remainingEdges; // re-use array
2086 // Delete all edges between partition1 and partition2
2087
2088 for (var i = newEdges.length - 1; i >= 0; i--) {
2089 var edge = newEdges[i];
2090 var src = edge[1];
2091 var tgt = edge[2];
2092
2093 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2094 newEdges.splice(i, 1);
2095 }
2096 } // All edges pointing to partition2 should now point to partition1
2097
2098
2099 for (var _i = 0; _i < newEdges.length; _i++) {
2100 var _edge = newEdges[_i];
2101
2102 if (_edge[1] === partition2) {
2103 // Check source
2104 newEdges[_i] = _edge.slice(); // copy
2105
2106 newEdges[_i][1] = partition1;
2107 } else if (_edge[2] === partition2) {
2108 // Check target
2109 newEdges[_i] = _edge.slice(); // copy
2110
2111 newEdges[_i][2] = partition1;
2112 }
2113 } // Move all nodes from partition2 to partition1
2114
2115
2116 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2117 if (nodeMap[_i2] === partition2) {
2118 nodeMap[_i2] = partition1;
2119 }
2120 }
2121
2122 return newEdges;
2123}; // Contracts a graph until we reach a certain number of meta nodes
2124
2125
2126var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2127 while (size > sizeLimit) {
2128 // Choose an edge randomly
2129 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2130
2131 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2132 size--;
2133 }
2134
2135 return remainingEdges;
2136};
2137
2138var elesfn$6 = {
2139 // Computes the minimum cut of an undirected graph
2140 // Returns the correct answer with high probability
2141 kargerStein: function kargerStein() {
2142 var _this = this;
2143
2144 var _this$byGroup = this.byGroup(),
2145 nodes = _this$byGroup.nodes,
2146 edges = _this$byGroup.edges;
2147
2148 edges.unmergeBy(function (edge) {
2149 return edge.isLoop();
2150 });
2151 var numNodes = nodes.length;
2152 var numEdges = edges.length;
2153 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2154 var stopSize = Math.floor(numNodes / sqrt2);
2155
2156 if (numNodes < 2) {
2157 error('At least 2 nodes are required for Karger-Stein algorithm');
2158 return undefined;
2159 } // Now store edge destination as indexes
2160 // Format for each edge (edge index, source node index, target node index)
2161
2162
2163 var edgeIndexes = [];
2164
2165 for (var i = 0; i < numEdges; i++) {
2166 var e = edges[i];
2167 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2168 } // We will store the best cut found here
2169
2170
2171 var minCutSize = Infinity;
2172 var minCutEdgeIndexes = [];
2173 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2174
2175 var metaNodeMap = new Array(numNodes);
2176 var metaNodeMap2 = new Array(numNodes);
2177
2178 var copyNodesMap = function copyNodesMap(from, to) {
2179 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2180 to[_i3] = from[_i3];
2181 }
2182 }; // Main loop
2183
2184
2185 for (var iter = 0; iter <= numIter; iter++) {
2186 // Reset meta node partition
2187 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2188 metaNodeMap[_i4] = _i4;
2189 } // Contract until stop point (stopSize nodes)
2190
2191
2192 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2193 var edgesState2 = edgesState.slice(); // copy
2194 // Create a copy of the colapsed nodes state
2195
2196 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2197
2198 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2199 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2200
2201 if (res1.length <= res2.length && res1.length < minCutSize) {
2202 minCutSize = res1.length;
2203 minCutEdgeIndexes = res1;
2204 copyNodesMap(metaNodeMap, minCutNodeMap);
2205 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2206 minCutSize = res2.length;
2207 minCutEdgeIndexes = res2;
2208 copyNodesMap(metaNodeMap2, minCutNodeMap);
2209 }
2210 } // end of main loop
2211 // Construct result
2212
2213
2214 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2215 return edges[e[0]];
2216 }));
2217 var partition1 = this.spawn();
2218 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2219
2220 var witnessNodePartition = minCutNodeMap[0];
2221
2222 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2223 var partitionId = minCutNodeMap[_i5];
2224 var node = nodes[_i5];
2225
2226 if (partitionId === witnessNodePartition) {
2227 partition1.merge(node);
2228 } else {
2229 partition2.merge(node);
2230 }
2231 } // construct components corresponding to each disjoint subset of nodes
2232
2233
2234 var constructComponent = function constructComponent(subset) {
2235 var component = _this.spawn();
2236
2237 subset.forEach(function (node) {
2238 component.merge(node);
2239 node.connectedEdges().forEach(function (edge) {
2240 // ensure edge is within calling collection and edge is not in cut
2241 if (_this.contains(edge) && !cut.contains(edge)) {
2242 component.merge(edge);
2243 }
2244 });
2245 });
2246 return component;
2247 };
2248
2249 var components = [constructComponent(partition1), constructComponent(partition2)];
2250 var ret = {
2251 cut: cut,
2252 components: components,
2253 // n.b. partitions are included to be compatible with the old api spec
2254 // (could be removed in a future major version)
2255 partition1: partition1,
2256 partition2: partition2
2257 };
2258 return ret;
2259 }
2260}; // elesfn
2261
2262var copyPosition = function copyPosition(p) {
2263 return {
2264 x: p.x,
2265 y: p.y
2266 };
2267};
2268var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
2269 return {
2270 x: p.x * zoom + pan.x,
2271 y: p.y * zoom + pan.y
2272 };
2273};
2274var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
2275 return {
2276 x: (p.x - pan.x) / zoom,
2277 y: (p.y - pan.y) / zoom
2278 };
2279};
2280var array2point = function array2point(arr) {
2281 return {
2282 x: arr[0],
2283 y: arr[1]
2284 };
2285};
2286var min = function min(arr) {
2287 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2288 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2289 var min = Infinity;
2290
2291 for (var i = begin; i < end; i++) {
2292 var val = arr[i];
2293
2294 if (isFinite(val)) {
2295 min = Math.min(val, min);
2296 }
2297 }
2298
2299 return min;
2300};
2301var max = function max(arr) {
2302 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2303 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2304 var max = -Infinity;
2305
2306 for (var i = begin; i < end; i++) {
2307 var val = arr[i];
2308
2309 if (isFinite(val)) {
2310 max = Math.max(val, max);
2311 }
2312 }
2313
2314 return max;
2315};
2316var mean = function mean(arr) {
2317 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2318 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2319 var total = 0;
2320 var n = 0;
2321
2322 for (var i = begin; i < end; i++) {
2323 var val = arr[i];
2324
2325 if (isFinite(val)) {
2326 total += val;
2327 n++;
2328 }
2329 }
2330
2331 return total / n;
2332};
2333var median = function median(arr) {
2334 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2335 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2336 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
2337 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
2338 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
2339
2340 if (copy) {
2341 arr = arr.slice(begin, end);
2342 } else {
2343 if (end < arr.length) {
2344 arr.splice(end, arr.length - end);
2345 }
2346
2347 if (begin > 0) {
2348 arr.splice(0, begin);
2349 }
2350 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
2351
2352
2353 var off = 0; // offset from non-finite values
2354
2355 for (var i = arr.length - 1; i >= 0; i--) {
2356 var v = arr[i];
2357
2358 if (includeHoles) {
2359 if (!isFinite(v)) {
2360 arr[i] = -Infinity;
2361 off++;
2362 }
2363 } else {
2364 // just remove it if we don't want to consider holes
2365 arr.splice(i, 1);
2366 }
2367 }
2368
2369 if (sort) {
2370 arr.sort(function (a, b) {
2371 return a - b;
2372 }); // requires copy = true if you don't want to change the orig
2373 }
2374
2375 var len = arr.length;
2376 var mid = Math.floor(len / 2);
2377
2378 if (len % 2 !== 0) {
2379 return arr[mid + 1 + off];
2380 } else {
2381 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
2382 }
2383};
2384var deg2rad = function deg2rad(deg) {
2385 return Math.PI * deg / 180;
2386};
2387var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
2388 return Math.atan2(dispY, dispX) - Math.PI / 2;
2389};
2390var log2 = Math.log2 || function (n) {
2391 return Math.log(n) / Math.log(2);
2392};
2393var signum = function signum(x) {
2394 if (x > 0) {
2395 return 1;
2396 } else if (x < 0) {
2397 return -1;
2398 } else {
2399 return 0;
2400 }
2401};
2402var dist = function dist(p1, p2) {
2403 return Math.sqrt(sqdist(p1, p2));
2404};
2405var sqdist = function sqdist(p1, p2) {
2406 var dx = p2.x - p1.x;
2407 var dy = p2.y - p1.y;
2408 return dx * dx + dy * dy;
2409};
2410var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
2411 var length = v.length; // First, get sum of all elements
2412
2413 var total = 0;
2414
2415 for (var i = 0; i < length; i++) {
2416 total += v[i];
2417 } // Now, divide each by the sum of all elements
2418
2419
2420 for (var _i = 0; _i < length; _i++) {
2421 v[_i] = v[_i] / total;
2422 }
2423
2424 return v;
2425};
2426
2427var qbezierAt = function qbezierAt(p0, p1, p2, t) {
2428 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
2429};
2430var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
2431 return {
2432 x: qbezierAt(p0.x, p1.x, p2.x, t),
2433 y: qbezierAt(p0.y, p1.y, p2.y, t)
2434 };
2435};
2436var lineAt = function lineAt(p0, p1, t, d) {
2437 var vec = {
2438 x: p1.x - p0.x,
2439 y: p1.y - p0.y
2440 };
2441 var vecDist = dist(p0, p1);
2442 var normVec = {
2443 x: vec.x / vecDist,
2444 y: vec.y / vecDist
2445 };
2446 t = t == null ? 0 : t;
2447 d = d != null ? d : t * vecDist;
2448 return {
2449 x: p0.x + normVec.x * d,
2450 y: p0.y + normVec.y * d
2451 };
2452};
2453var bound = function bound(min, val, max) {
2454 return Math.max(min, Math.min(max, val));
2455}; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
2456
2457var makeBoundingBox = function makeBoundingBox(bb) {
2458 if (bb == null) {
2459 return {
2460 x1: Infinity,
2461 y1: Infinity,
2462 x2: -Infinity,
2463 y2: -Infinity,
2464 w: 0,
2465 h: 0
2466 };
2467 } else if (bb.x1 != null && bb.y1 != null) {
2468 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
2469 return {
2470 x1: bb.x1,
2471 y1: bb.y1,
2472 x2: bb.x2,
2473 y2: bb.y2,
2474 w: bb.x2 - bb.x1,
2475 h: bb.y2 - bb.y1
2476 };
2477 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
2478 return {
2479 x1: bb.x1,
2480 y1: bb.y1,
2481 x2: bb.x1 + bb.w,
2482 y2: bb.y1 + bb.h,
2483 w: bb.w,
2484 h: bb.h
2485 };
2486 }
2487 }
2488};
2489var copyBoundingBox = function copyBoundingBox(bb) {
2490 return {
2491 x1: bb.x1,
2492 x2: bb.x2,
2493 w: bb.w,
2494 y1: bb.y1,
2495 y2: bb.y2,
2496 h: bb.h
2497 };
2498};
2499var clearBoundingBox = function clearBoundingBox(bb) {
2500 bb.x1 = Infinity;
2501 bb.y1 = Infinity;
2502 bb.x2 = -Infinity;
2503 bb.y2 = -Infinity;
2504 bb.w = 0;
2505 bb.h = 0;
2506};
2507var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
2508 // update bb1 with bb2 bounds
2509 bb1.x1 = Math.min(bb1.x1, bb2.x1);
2510 bb1.x2 = Math.max(bb1.x2, bb2.x2);
2511 bb1.w = bb1.x2 - bb1.x1;
2512 bb1.y1 = Math.min(bb1.y1, bb2.y1);
2513 bb1.y2 = Math.max(bb1.y2, bb2.y2);
2514 bb1.h = bb1.y2 - bb1.y1;
2515};
2516var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
2517 bb.x1 = Math.min(bb.x1, x);
2518 bb.x2 = Math.max(bb.x2, x);
2519 bb.w = bb.x2 - bb.x1;
2520 bb.y1 = Math.min(bb.y1, y);
2521 bb.y2 = Math.max(bb.y2, y);
2522 bb.h = bb.y2 - bb.y1;
2523};
2524var expandBoundingBox = function expandBoundingBox(bb) {
2525 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2526 bb.x1 -= padding;
2527 bb.x2 += padding;
2528 bb.y1 -= padding;
2529 bb.y2 += padding;
2530 bb.w = bb.x2 - bb.x1;
2531 bb.h = bb.y2 - bb.y1;
2532 return bb;
2533};
2534var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
2535 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
2536 var top, right, bottom, left;
2537
2538 if (padding.length === 1) {
2539 top = right = bottom = left = padding[0];
2540 } else if (padding.length === 2) {
2541 top = bottom = padding[0];
2542 left = right = padding[1];
2543 } else if (padding.length === 4) {
2544 var _padding = _slicedToArray(padding, 4);
2545
2546 top = _padding[0];
2547 right = _padding[1];
2548 bottom = _padding[2];
2549 left = _padding[3];
2550 }
2551
2552 bb.x1 -= left;
2553 bb.x2 += right;
2554 bb.y1 -= top;
2555 bb.y2 += bottom;
2556 bb.w = bb.x2 - bb.x1;
2557 bb.h = bb.y2 - bb.y1;
2558 return bb;
2559};
2560
2561var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
2562 bb1.x1 = bb2.x1;
2563 bb1.y1 = bb2.y1;
2564 bb1.x2 = bb2.x2;
2565 bb1.y2 = bb2.y2;
2566 bb1.w = bb1.x2 - bb1.x1;
2567 bb1.h = bb1.y2 - bb1.y1;
2568};
2569var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
2570 // case: one bb to right of other
2571 if (bb1.x1 > bb2.x2) {
2572 return false;
2573 }
2574
2575 if (bb2.x1 > bb1.x2) {
2576 return false;
2577 } // case: one bb to left of other
2578
2579
2580 if (bb1.x2 < bb2.x1) {
2581 return false;
2582 }
2583
2584 if (bb2.x2 < bb1.x1) {
2585 return false;
2586 } // case: one bb above other
2587
2588
2589 if (bb1.y2 < bb2.y1) {
2590 return false;
2591 }
2592
2593 if (bb2.y2 < bb1.y1) {
2594 return false;
2595 } // case: one bb below other
2596
2597
2598 if (bb1.y1 > bb2.y2) {
2599 return false;
2600 }
2601
2602 if (bb2.y1 > bb1.y2) {
2603 return false;
2604 } // otherwise, must have some overlap
2605
2606
2607 return true;
2608};
2609var inBoundingBox = function inBoundingBox(bb, x, y) {
2610 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
2611};
2612var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
2613 return inBoundingBox(bb, pt.x, pt.y);
2614};
2615var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
2616 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
2617};
2618var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
2619 var cornerRadius = getRoundRectangleRadius(width, height);
2620 var halfWidth = width / 2;
2621 var halfHeight = height / 2; // Check intersections with straight line segments
2622
2623 var straightLineIntersections; // Top segment, left to right
2624
2625 {
2626 var topStartX = nodeX - halfWidth + cornerRadius - padding;
2627 var topStartY = nodeY - halfHeight - padding;
2628 var topEndX = nodeX + halfWidth - cornerRadius + padding;
2629 var topEndY = topStartY;
2630 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
2631
2632 if (straightLineIntersections.length > 0) {
2633 return straightLineIntersections;
2634 }
2635 } // Right segment, top to bottom
2636
2637 {
2638 var rightStartX = nodeX + halfWidth + padding;
2639 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
2640 var rightEndX = rightStartX;
2641 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
2642 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
2643
2644 if (straightLineIntersections.length > 0) {
2645 return straightLineIntersections;
2646 }
2647 } // Bottom segment, left to right
2648
2649 {
2650 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
2651 var bottomStartY = nodeY + halfHeight + padding;
2652 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
2653 var bottomEndY = bottomStartY;
2654 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
2655
2656 if (straightLineIntersections.length > 0) {
2657 return straightLineIntersections;
2658 }
2659 } // Left segment, top to bottom
2660
2661 {
2662 var leftStartX = nodeX - halfWidth - padding;
2663 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
2664 var leftEndX = leftStartX;
2665 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
2666 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
2667
2668 if (straightLineIntersections.length > 0) {
2669 return straightLineIntersections;
2670 }
2671 } // Check intersections with arc segments
2672
2673 var arcIntersections; // Top Left
2674
2675 {
2676 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
2677 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
2678 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2679
2680 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
2681 return [arcIntersections[0], arcIntersections[1]];
2682 }
2683 } // Top Right
2684
2685 {
2686 var topRightCenterX = nodeX + halfWidth - cornerRadius;
2687 var topRightCenterY = nodeY - halfHeight + cornerRadius;
2688 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2689
2690 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
2691 return [arcIntersections[0], arcIntersections[1]];
2692 }
2693 } // Bottom Right
2694
2695 {
2696 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
2697 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
2698 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2699
2700 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
2701 return [arcIntersections[0], arcIntersections[1]];
2702 }
2703 } // Bottom Left
2704
2705 {
2706 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
2707 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
2708 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2709
2710 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
2711 return [arcIntersections[0], arcIntersections[1]];
2712 }
2713 }
2714 return []; // if nothing
2715};
2716var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
2717 var t = tolerance;
2718 var x1 = Math.min(lx1, lx2);
2719 var x2 = Math.max(lx1, lx2);
2720 var y1 = Math.min(ly1, ly2);
2721 var y2 = Math.max(ly1, ly2);
2722 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
2723};
2724var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
2725 var bb = {
2726 x1: Math.min(x1, x3, x2) - tolerance,
2727 x2: Math.max(x1, x3, x2) + tolerance,
2728 y1: Math.min(y1, y3, y2) - tolerance,
2729 y2: Math.max(y1, y3, y2) + tolerance
2730 }; // if outside the rough bounding box for the bezier, then it can't be a hit
2731
2732 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
2733 // console.log('bezier out of rough bb')
2734 return false;
2735 } else {
2736 // console.log('do more expensive check');
2737 return true;
2738 }
2739};
2740var solveQuadratic = function solveQuadratic(a, b, c, val) {
2741 c -= val;
2742 var r = b * b - 4 * a * c;
2743
2744 if (r < 0) {
2745 return [];
2746 }
2747
2748 var sqrtR = Math.sqrt(r);
2749 var denom = 2 * a;
2750 var root1 = (-b + sqrtR) / denom;
2751 var root2 = (-b - sqrtR) / denom;
2752 return [root1, root2];
2753};
2754var solveCubic = function solveCubic(a, b, c, d, result) {
2755 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
2756 // r is the real component, i is the imaginary component
2757 // An implementation of the Cardano method from the year 1545
2758 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
2759 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
2760
2761 if (a === 0) {
2762 a = epsilon;
2763 }
2764
2765 b /= a;
2766 c /= a;
2767 d /= a;
2768 var discriminant, q, r, dum1, s, t, term1, r13;
2769 q = (3.0 * c - b * b) / 9.0;
2770 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
2771 r /= 54.0;
2772 discriminant = q * q * q + r * r;
2773 result[1] = 0;
2774 term1 = b / 3.0;
2775
2776 if (discriminant > 0) {
2777 s = r + Math.sqrt(discriminant);
2778 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
2779 t = r - Math.sqrt(discriminant);
2780 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
2781 result[0] = -term1 + s + t;
2782 term1 += (s + t) / 2.0;
2783 result[4] = result[2] = -term1;
2784 term1 = Math.sqrt(3.0) * (-t + s) / 2;
2785 result[3] = term1;
2786 result[5] = -term1;
2787 return;
2788 }
2789
2790 result[5] = result[3] = 0;
2791
2792 if (discriminant === 0) {
2793 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
2794 result[0] = -term1 + 2.0 * r13;
2795 result[4] = result[2] = -(r13 + term1);
2796 return;
2797 }
2798
2799 q = -q;
2800 dum1 = q * q * q;
2801 dum1 = Math.acos(r / Math.sqrt(dum1));
2802 r13 = 2.0 * Math.sqrt(q);
2803 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
2804 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
2805 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
2806 return;
2807};
2808var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
2809 // Find minimum distance by using the minimum of the distance
2810 // function between the given point and the curve
2811 // This gives the coefficients of the resulting cubic equation
2812 // whose roots tell us where a possible minimum is
2813 // (Coefficients are divided by 4)
2814 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;
2815 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;
2816 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;
2817 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);
2818
2819 var roots = []; // Use the cubic solving algorithm
2820
2821 solveCubic(a, b, c, d, roots);
2822 var zeroThreshold = 0.0000001;
2823 var params = [];
2824
2825 for (var index = 0; index < 6; index += 2) {
2826 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
2827 params.push(roots[index]);
2828 }
2829 }
2830
2831 params.push(1.0);
2832 params.push(0.0);
2833 var minDistanceSquared = -1;
2834 var curX, curY, distSquared;
2835
2836 for (var i = 0; i < params.length; i++) {
2837 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
2838 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
2839 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
2840
2841 if (minDistanceSquared >= 0) {
2842 if (distSquared < minDistanceSquared) {
2843 minDistanceSquared = distSquared;
2844 }
2845 } else {
2846 minDistanceSquared = distSquared;
2847 }
2848 }
2849
2850 return minDistanceSquared;
2851};
2852var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
2853 var offset = [x - x1, y - y1];
2854 var line = [x2 - x1, y2 - y1];
2855 var lineSq = line[0] * line[0] + line[1] * line[1];
2856 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
2857 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
2858 var adjSq = dotProduct * dotProduct / lineSq;
2859
2860 if (dotProduct < 0) {
2861 return hypSq;
2862 }
2863
2864 if (adjSq > lineSq) {
2865 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
2866 }
2867
2868 return hypSq - adjSq;
2869};
2870var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
2871 var x1, y1, x2, y2;
2872 var y3; // Intersect with vertical line through (x, y)
2873
2874 var up = 0; // let down = 0;
2875
2876 for (var i = 0; i < points.length / 2; i++) {
2877 x1 = points[i * 2];
2878 y1 = points[i * 2 + 1];
2879
2880 if (i + 1 < points.length / 2) {
2881 x2 = points[(i + 1) * 2];
2882 y2 = points[(i + 1) * 2 + 1];
2883 } else {
2884 x2 = points[(i + 1 - points.length / 2) * 2];
2885 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
2886 }
2887
2888 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
2889 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
2890
2891 if (y3 > y) {
2892 up++;
2893 } // if( y3 < y ){
2894 // down++;
2895 // }
2896
2897 } else {
2898 continue;
2899 }
2900 }
2901
2902 if (up % 2 === 0) {
2903 return false;
2904 } else {
2905 return true;
2906 }
2907};
2908var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
2909 var transformedPoints = new Array(basePoints.length); // Gives negative angle
2910
2911 var angle;
2912
2913 if (direction[0] != null) {
2914 angle = Math.atan(direction[1] / direction[0]);
2915
2916 if (direction[0] < 0) {
2917 angle = angle + Math.PI / 2;
2918 } else {
2919 angle = -angle - Math.PI / 2;
2920 }
2921 } else {
2922 angle = direction;
2923 }
2924
2925 var cos = Math.cos(-angle);
2926 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
2927
2928 for (var i = 0; i < transformedPoints.length / 2; i++) {
2929 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
2930 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
2931 transformedPoints[i * 2] += centerX;
2932 transformedPoints[i * 2 + 1] += centerY;
2933 }
2934
2935 var points;
2936
2937 if (padding > 0) {
2938 var expandedLineSet = expandPolygon(transformedPoints, -padding);
2939 points = joinLines(expandedLineSet);
2940 } else {
2941 points = transformedPoints;
2942 }
2943
2944 return pointInsidePolygonPoints(x, y, points);
2945};
2946var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
2947 var cutPolygonPoints = new Array(basePoints.length);
2948 var halfW = width / 2;
2949 var halfH = height / 2;
2950 var cornerRadius = getRoundPolygonRadius(width, height);
2951 var squaredCornerRadius = cornerRadius * cornerRadius;
2952
2953 for (var i = 0; i < basePoints.length / 4; i++) {
2954 var sourceUv = void 0,
2955 destUv = void 0;
2956
2957 if (i === 0) {
2958 sourceUv = basePoints.length - 2;
2959 } else {
2960 sourceUv = i * 4 - 2;
2961 }
2962
2963 destUv = i * 4 + 2;
2964 var px = centerX + halfW * basePoints[i * 4];
2965 var py = centerY + halfH * basePoints[i * 4 + 1];
2966 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
2967 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
2968 var cp0x = px - offset * basePoints[sourceUv];
2969 var cp0y = py - offset * basePoints[sourceUv + 1];
2970 var cp1x = px + offset * basePoints[destUv];
2971 var cp1y = py + offset * basePoints[destUv + 1];
2972 cutPolygonPoints[i * 4] = cp0x;
2973 cutPolygonPoints[i * 4 + 1] = cp0y;
2974 cutPolygonPoints[i * 4 + 2] = cp1x;
2975 cutPolygonPoints[i * 4 + 3] = cp1y;
2976 var orthx = basePoints[sourceUv + 1];
2977 var orthy = -basePoints[sourceUv];
2978 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
2979
2980 if (cosAlpha < 0) {
2981 orthx *= -1;
2982 orthy *= -1;
2983 }
2984
2985 var cx = cp0x + orthx * cornerRadius;
2986 var cy = cp0y + orthy * cornerRadius;
2987 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
2988
2989 if (squaredDistance <= squaredCornerRadius) {
2990 return true;
2991 }
2992 }
2993
2994 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
2995};
2996var joinLines = function joinLines(lineSet) {
2997 var vertices = new Array(lineSet.length / 2);
2998 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
2999 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3000
3001 for (var i = 0; i < lineSet.length / 4; i++) {
3002 currentLineStartX = lineSet[i * 4];
3003 currentLineStartY = lineSet[i * 4 + 1];
3004 currentLineEndX = lineSet[i * 4 + 2];
3005 currentLineEndY = lineSet[i * 4 + 3];
3006
3007 if (i < lineSet.length / 4 - 1) {
3008 nextLineStartX = lineSet[(i + 1) * 4];
3009 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3010 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3011 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3012 } else {
3013 nextLineStartX = lineSet[0];
3014 nextLineStartY = lineSet[1];
3015 nextLineEndX = lineSet[2];
3016 nextLineEndY = lineSet[3];
3017 }
3018
3019 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3020 vertices[i * 2] = intersection[0];
3021 vertices[i * 2 + 1] = intersection[1];
3022 }
3023
3024 return vertices;
3025};
3026var expandPolygon = function expandPolygon(points, pad) {
3027 var expandedLineSet = new Array(points.length * 2);
3028 var currentPointX, currentPointY, nextPointX, nextPointY;
3029
3030 for (var i = 0; i < points.length / 2; i++) {
3031 currentPointX = points[i * 2];
3032 currentPointY = points[i * 2 + 1];
3033
3034 if (i < points.length / 2 - 1) {
3035 nextPointX = points[(i + 1) * 2];
3036 nextPointY = points[(i + 1) * 2 + 1];
3037 } else {
3038 nextPointX = points[0];
3039 nextPointY = points[1];
3040 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3041 // Assume CCW polygon winding
3042
3043
3044 var offsetX = nextPointY - currentPointY;
3045 var offsetY = -(nextPointX - currentPointX); // Normalize
3046
3047 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3048 var normalizedOffsetX = offsetX / offsetLength;
3049 var normalizedOffsetY = offsetY / offsetLength;
3050 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3051 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3052 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3053 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3054 }
3055
3056 return expandedLineSet;
3057};
3058var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3059 var dispX = centerX - x;
3060 var dispY = centerY - y;
3061 dispX /= ellipseWradius;
3062 dispY /= ellipseHradius;
3063 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3064 var newLength = len - 1;
3065
3066 if (newLength < 0) {
3067 return [];
3068 }
3069
3070 var lenProportion = newLength / len;
3071 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3072};
3073var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3074 x -= centerX;
3075 y -= centerY;
3076 x /= width / 2 + padding;
3077 y /= height / 2 + padding;
3078 return x * x + y * y <= 1;
3079}; // Returns intersections of increasing distance from line's start point
3080
3081var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3082 // Calculate d, direction vector of line
3083 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3084
3085 var f = [x1 - centerX, y1 - centerY];
3086 var a = d[0] * d[0] + d[1] * d[1];
3087 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3088 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3089 var discriminant = b * b - 4 * a * c;
3090
3091 if (discriminant < 0) {
3092 return [];
3093 }
3094
3095 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3096 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3097 var tMin = Math.min(t1, t2);
3098 var tMax = Math.max(t1, t2);
3099 var inRangeParams = [];
3100
3101 if (tMin >= 0 && tMin <= 1) {
3102 inRangeParams.push(tMin);
3103 }
3104
3105 if (tMax >= 0 && tMax <= 1) {
3106 inRangeParams.push(tMax);
3107 }
3108
3109 if (inRangeParams.length === 0) {
3110 return [];
3111 }
3112
3113 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3114 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3115
3116 if (inRangeParams.length > 1) {
3117 if (inRangeParams[0] == inRangeParams[1]) {
3118 return [nearIntersectionX, nearIntersectionY];
3119 } else {
3120 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3121 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3122 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3123 }
3124 } else {
3125 return [nearIntersectionX, nearIntersectionY];
3126 }
3127};
3128var midOfThree = function midOfThree(a, b, c) {
3129 if (b <= a && a <= c || c <= a && a <= b) {
3130 return a;
3131 } else if (a <= b && b <= c || c <= b && b <= a) {
3132 return b;
3133 } else {
3134 return c;
3135 }
3136}; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3137
3138var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3139 var dx13 = x1 - x3;
3140 var dx21 = x2 - x1;
3141 var dx43 = x4 - x3;
3142 var dy13 = y1 - y3;
3143 var dy21 = y2 - y1;
3144 var dy43 = y4 - y3;
3145 var ua_t = dx43 * dy13 - dy43 * dx13;
3146 var ub_t = dx21 * dy13 - dy21 * dx13;
3147 var u_b = dy43 * dx21 - dx43 * dy21;
3148
3149 if (u_b !== 0) {
3150 var ua = ua_t / u_b;
3151 var ub = ub_t / u_b;
3152 var flptThreshold = 0.001;
3153
3154 var _min = 0 - flptThreshold;
3155
3156 var _max = 1 + flptThreshold;
3157
3158 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3159 return [x1 + ua * dx21, y1 + ua * dy21];
3160 } else {
3161 if (!infiniteLines) {
3162 return [];
3163 } else {
3164 return [x1 + ua * dx21, y1 + ua * dy21];
3165 }
3166 }
3167 } else {
3168 if (ua_t === 0 || ub_t === 0) {
3169 // Parallel, coincident lines. Check if overlap
3170 // Check endpoint of second line
3171 if (midOfThree(x1, x2, x4) === x4) {
3172 return [x4, y4];
3173 } // Check start point of second line
3174
3175
3176 if (midOfThree(x1, x2, x3) === x3) {
3177 return [x3, y3];
3178 } // Endpoint of first line
3179
3180
3181 if (midOfThree(x3, x4, x2) === x2) {
3182 return [x2, y2];
3183 }
3184
3185 return [];
3186 } else {
3187 // Parallel, non-coincident
3188 return [];
3189 }
3190 }
3191}; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3192// intersect a node polygon (pts transformed)
3193//
3194// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3195// intersect the points (no transform)
3196
3197var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3198 var intersections = [];
3199 var intersection;
3200 var transformedPoints = new Array(basePoints.length);
3201 var doTransform = true;
3202
3203 if (width == null) {
3204 doTransform = false;
3205 }
3206
3207 var points;
3208
3209 if (doTransform) {
3210 for (var i = 0; i < transformedPoints.length / 2; i++) {
3211 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3212 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3213 }
3214
3215 if (padding > 0) {
3216 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3217 points = joinLines(expandedLineSet);
3218 } else {
3219 points = transformedPoints;
3220 }
3221 } else {
3222 points = basePoints;
3223 }
3224
3225 var currentX, currentY, nextX, nextY;
3226
3227 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3228 currentX = points[_i2 * 2];
3229 currentY = points[_i2 * 2 + 1];
3230
3231 if (_i2 < points.length / 2 - 1) {
3232 nextX = points[(_i2 + 1) * 2];
3233 nextY = points[(_i2 + 1) * 2 + 1];
3234 } else {
3235 nextX = points[0];
3236 nextY = points[1];
3237 }
3238
3239 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3240
3241 if (intersection.length !== 0) {
3242 intersections.push(intersection[0], intersection[1]);
3243 }
3244 }
3245
3246 return intersections;
3247};
3248var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3249 var intersections = [];
3250 var intersection;
3251 var lines = new Array(basePoints.length);
3252 var halfW = width / 2;
3253 var halfH = height / 2;
3254 var cornerRadius = getRoundPolygonRadius(width, height);
3255
3256 for (var i = 0; i < basePoints.length / 4; i++) {
3257 var sourceUv = void 0,
3258 destUv = void 0;
3259
3260 if (i === 0) {
3261 sourceUv = basePoints.length - 2;
3262 } else {
3263 sourceUv = i * 4 - 2;
3264 }
3265
3266 destUv = i * 4 + 2;
3267 var px = centerX + halfW * basePoints[i * 4];
3268 var py = centerY + halfH * basePoints[i * 4 + 1];
3269 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3270 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3271 var cp0x = px - offset * basePoints[sourceUv];
3272 var cp0y = py - offset * basePoints[sourceUv + 1];
3273 var cp1x = px + offset * basePoints[destUv];
3274 var cp1y = py + offset * basePoints[destUv + 1];
3275
3276 if (i === 0) {
3277 lines[basePoints.length - 2] = cp0x;
3278 lines[basePoints.length - 1] = cp0y;
3279 } else {
3280 lines[i * 4 - 2] = cp0x;
3281 lines[i * 4 - 1] = cp0y;
3282 }
3283
3284 lines[i * 4] = cp1x;
3285 lines[i * 4 + 1] = cp1y;
3286 var orthx = basePoints[sourceUv + 1];
3287 var orthy = -basePoints[sourceUv];
3288 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3289
3290 if (cosAlpha < 0) {
3291 orthx *= -1;
3292 orthy *= -1;
3293 }
3294
3295 var cx = cp0x + orthx * cornerRadius;
3296 var cy = cp0y + orthy * cornerRadius;
3297 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
3298
3299 if (intersection.length !== 0) {
3300 intersections.push(intersection[0], intersection[1]);
3301 }
3302 }
3303
3304 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
3305 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
3306
3307 if (intersection.length !== 0) {
3308 intersections.push(intersection[0], intersection[1]);
3309 }
3310 }
3311
3312 if (intersections.length > 2) {
3313 var lowestIntersection = [intersections[0], intersections[1]];
3314 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3315
3316 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
3317 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
3318
3319 if (squaredDistance <= lowestSquaredDistance) {
3320 lowestIntersection[0] = intersections[_i4 * 2];
3321 lowestIntersection[1] = intersections[_i4 * 2 + 1];
3322 lowestSquaredDistance = squaredDistance;
3323 }
3324 }
3325
3326 return lowestIntersection;
3327 }
3328
3329 return intersections;
3330};
3331var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3332 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3333 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3334 var lenRatio = (length - amount) / length;
3335
3336 if (lenRatio < 0) {
3337 lenRatio = 0.00001;
3338 }
3339
3340 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3341};
3342var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3343 var points = generateUnitNgonPoints(sides, rotationRadians);
3344 points = fitPolygonToSquare(points);
3345 return points;
3346};
3347var fitPolygonToSquare = function fitPolygonToSquare(points) {
3348 var x, y;
3349 var sides = points.length / 2;
3350 var minX = Infinity,
3351 minY = Infinity,
3352 maxX = -Infinity,
3353 maxY = -Infinity;
3354
3355 for (var i = 0; i < sides; i++) {
3356 x = points[2 * i];
3357 y = points[2 * i + 1];
3358 minX = Math.min(minX, x);
3359 maxX = Math.max(maxX, x);
3360 minY = Math.min(minY, y);
3361 maxY = Math.max(maxY, y);
3362 } // stretch factors
3363
3364
3365 var sx = 2 / (maxX - minX);
3366 var sy = 2 / (maxY - minY);
3367
3368 for (var _i5 = 0; _i5 < sides; _i5++) {
3369 x = points[2 * _i5] = points[2 * _i5] * sx;
3370 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
3371 minX = Math.min(minX, x);
3372 maxX = Math.max(maxX, x);
3373 minY = Math.min(minY, y);
3374 maxY = Math.max(maxY, y);
3375 }
3376
3377 if (minY < -1) {
3378 for (var _i6 = 0; _i6 < sides; _i6++) {
3379 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
3380 }
3381 }
3382
3383 return points;
3384};
3385var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
3386 var increment = 1.0 / sides * 2 * Math.PI;
3387 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
3388 startAngle += rotationRadians;
3389 var points = new Array(sides * 2);
3390 var currentAngle;
3391
3392 for (var i = 0; i < sides; i++) {
3393 currentAngle = i * increment + startAngle;
3394 points[2 * i] = Math.cos(currentAngle); // x
3395
3396 points[2 * i + 1] = Math.sin(-currentAngle); // y
3397 }
3398
3399 return points;
3400}; // Set the default radius, unless half of width or height is smaller than default
3401
3402var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
3403 return Math.min(width / 4, height / 4, 8);
3404}; // Set the default radius
3405
3406var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
3407 return Math.min(width / 10, height / 10, 8);
3408};
3409var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
3410 return 8;
3411};
3412var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
3413 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
3414}; // get curve width, height, and control point position offsets as a percentage of node height / width
3415
3416var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
3417 return {
3418 heightOffset: Math.min(15, 0.05 * height),
3419 widthOffset: Math.min(100, 0.25 * width),
3420 ctrlPtOffsetPct: 0.05
3421 };
3422};
3423
3424var pageRankDefaults = defaults({
3425 dampingFactor: 0.8,
3426 precision: 0.000001,
3427 iterations: 200,
3428 weight: function weight(edge) {
3429 return 1;
3430 }
3431});
3432var elesfn$7 = {
3433 pageRank: function pageRank(options) {
3434 var _pageRankDefaults = pageRankDefaults(options),
3435 dampingFactor = _pageRankDefaults.dampingFactor,
3436 precision = _pageRankDefaults.precision,
3437 iterations = _pageRankDefaults.iterations,
3438 weight = _pageRankDefaults.weight;
3439
3440 var cy = this._private.cy;
3441
3442 var _this$byGroup = this.byGroup(),
3443 nodes = _this$byGroup.nodes,
3444 edges = _this$byGroup.edges;
3445
3446 var numNodes = nodes.length;
3447 var numNodesSqd = numNodes * numNodes;
3448 var numEdges = edges.length; // Construct transposed adjacency matrix
3449 // First lets have a zeroed matrix of the right size
3450 // We'll also keep track of the sum of each column
3451
3452 var matrix = new Array(numNodesSqd);
3453 var columnSum = new Array(numNodes);
3454 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
3455
3456 for (var i = 0; i < numNodes; i++) {
3457 for (var j = 0; j < numNodes; j++) {
3458 var n = i * numNodes + j;
3459 matrix[n] = 0;
3460 }
3461
3462 columnSum[i] = 0;
3463 } // Now, process edges
3464
3465
3466 for (var _i = 0; _i < numEdges; _i++) {
3467 var edge = edges[_i];
3468 var srcId = edge.data('source');
3469 var tgtId = edge.data('target'); // Don't include loops in the matrix
3470
3471 if (srcId === tgtId) {
3472 continue;
3473 }
3474
3475 var s = nodes.indexOfId(srcId);
3476 var t = nodes.indexOfId(tgtId);
3477 var w = weight(edge);
3478
3479 var _n = t * numNodes + s; // Update matrix
3480
3481
3482 matrix[_n] += w; // Update column sum
3483
3484 columnSum[s] += w;
3485 } // Add additional probability based on damping factor
3486 // Also, take into account columns that have sum = 0
3487
3488
3489 var p = 1.0 / numNodes + additionalProb; // Shorthand
3490 // Traverse matrix, column by column
3491
3492 for (var _j = 0; _j < numNodes; _j++) {
3493 if (columnSum[_j] === 0) {
3494 // No 'links' out from node jth, assume equal probability for each possible node
3495 for (var _i2 = 0; _i2 < numNodes; _i2++) {
3496 var _n2 = _i2 * numNodes + _j;
3497
3498 matrix[_n2] = p;
3499 }
3500 } else {
3501 // Node jth has outgoing link, compute normalized probabilities
3502 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3503 var _n3 = _i3 * numNodes + _j;
3504
3505 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
3506 }
3507 }
3508 } // Compute dominant eigenvector using power method
3509
3510
3511 var eigenvector = new Array(numNodes);
3512 var temp = new Array(numNodes);
3513 var previous; // Start with a vector of all 1's
3514 // Also, initialize a null vector which will be used as shorthand
3515
3516 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3517 eigenvector[_i4] = 1;
3518 }
3519
3520 for (var iter = 0; iter < iterations; iter++) {
3521 // Temp array with all 0's
3522 for (var _i5 = 0; _i5 < numNodes; _i5++) {
3523 temp[_i5] = 0;
3524 } // Multiply matrix with previous result
3525
3526
3527 for (var _i6 = 0; _i6 < numNodes; _i6++) {
3528 for (var _j2 = 0; _j2 < numNodes; _j2++) {
3529 var _n4 = _i6 * numNodes + _j2;
3530
3531 temp[_i6] += matrix[_n4] * eigenvector[_j2];
3532 }
3533 }
3534
3535 inPlaceSumNormalize(temp);
3536 previous = eigenvector;
3537 eigenvector = temp;
3538 temp = previous;
3539 var diff = 0; // Compute difference (squared module) of both vectors
3540
3541 for (var _i7 = 0; _i7 < numNodes; _i7++) {
3542 var delta = previous[_i7] - eigenvector[_i7];
3543 diff += delta * delta;
3544 } // If difference is less than the desired threshold, stop iterating
3545
3546
3547 if (diff < precision) {
3548 break;
3549 }
3550 } // Construct result
3551
3552
3553 var res = {
3554 rank: function rank(node) {
3555 node = cy.collection(node)[0];
3556 return eigenvector[nodes.indexOf(node)];
3557 }
3558 };
3559 return res;
3560 } // pageRank
3561
3562}; // elesfn
3563
3564var defaults$1 = defaults({
3565 root: null,
3566 weight: function weight(edge) {
3567 return 1;
3568 },
3569 directed: false,
3570 alpha: 0
3571});
3572var elesfn$8 = {
3573 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
3574 options = defaults$1(options);
3575 var cy = this.cy();
3576 var nodes = this.nodes();
3577 var numNodes = nodes.length;
3578
3579 if (!options.directed) {
3580 var degrees = {};
3581 var maxDegree = 0;
3582
3583 for (var i = 0; i < numNodes; i++) {
3584 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
3585
3586 options.root = node;
3587 var currDegree = this.degreeCentrality(options);
3588
3589 if (maxDegree < currDegree.degree) {
3590 maxDegree = currDegree.degree;
3591 }
3592
3593 degrees[node.id()] = currDegree.degree;
3594 }
3595
3596 return {
3597 degree: function degree(node) {
3598 if (maxDegree === 0) {
3599 return 0;
3600 }
3601
3602 if (string(node)) {
3603 // from is a selector string
3604 node = cy.filter(node);
3605 }
3606
3607 return degrees[node.id()] / maxDegree;
3608 }
3609 };
3610 } else {
3611 var indegrees = {};
3612 var outdegrees = {};
3613 var maxIndegree = 0;
3614 var maxOutdegree = 0;
3615
3616 for (var _i = 0; _i < numNodes; _i++) {
3617 var _node = nodes[_i];
3618
3619 var id = _node.id(); // add current node to the current options object and call degreeCentrality
3620
3621
3622 options.root = _node;
3623
3624 var _currDegree = this.degreeCentrality(options);
3625
3626 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
3627 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
3628 indegrees[id] = _currDegree.indegree;
3629 outdegrees[id] = _currDegree.outdegree;
3630 }
3631
3632 return {
3633 indegree: function indegree(node) {
3634 if (maxIndegree == 0) {
3635 return 0;
3636 }
3637
3638 if (string(node)) {
3639 // from is a selector string
3640 node = cy.filter(node);
3641 }
3642
3643 return indegrees[node.id()] / maxIndegree;
3644 },
3645 outdegree: function outdegree(node) {
3646 if (maxOutdegree === 0) {
3647 return 0;
3648 }
3649
3650 if (string(node)) {
3651 // from is a selector string
3652 node = cy.filter(node);
3653 }
3654
3655 return outdegrees[node.id()] / maxOutdegree;
3656 }
3657 };
3658 }
3659 },
3660 // degreeCentralityNormalized
3661 // Implemented from the algorithm in Opsahl's paper
3662 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
3663 // check the heading 2 "Degree"
3664 degreeCentrality: function degreeCentrality(options) {
3665 options = defaults$1(options);
3666 var cy = this.cy();
3667 var callingEles = this;
3668 var _options = options,
3669 root = _options.root,
3670 weight = _options.weight,
3671 directed = _options.directed,
3672 alpha = _options.alpha;
3673 root = cy.collection(root)[0];
3674
3675 if (!directed) {
3676 var connEdges = root.connectedEdges().intersection(callingEles);
3677 var k = connEdges.length;
3678 var s = 0; // Now, sum edge weights
3679
3680 for (var i = 0; i < connEdges.length; i++) {
3681 s += weight(connEdges[i]);
3682 }
3683
3684 return {
3685 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
3686 };
3687 } else {
3688 var edges = root.connectedEdges();
3689 var incoming = edges.filter(function (edge) {
3690 return edge.target().same(root) && callingEles.has(edge);
3691 });
3692 var outgoing = edges.filter(function (edge) {
3693 return edge.source().same(root) && callingEles.has(edge);
3694 });
3695 var k_in = incoming.length;
3696 var k_out = outgoing.length;
3697 var s_in = 0;
3698 var s_out = 0; // Now, sum incoming edge weights
3699
3700 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
3701 s_in += weight(incoming[_i2]);
3702 } // Now, sum outgoing edge weights
3703
3704
3705 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
3706 s_out += weight(outgoing[_i3]);
3707 }
3708
3709 return {
3710 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
3711 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
3712 };
3713 }
3714 } // degreeCentrality
3715
3716}; // elesfn
3717// nice, short mathemathical alias
3718
3719elesfn$8.dc = elesfn$8.degreeCentrality;
3720elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
3721
3722var defaults$2 = defaults({
3723 harmonic: true,
3724 weight: function weight() {
3725 return 1;
3726 },
3727 directed: false,
3728 root: null
3729});
3730var elesfn$9 = {
3731 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
3732 var _defaults = defaults$2(options),
3733 harmonic = _defaults.harmonic,
3734 weight = _defaults.weight,
3735 directed = _defaults.directed;
3736
3737 var cy = this.cy();
3738 var closenesses = {};
3739 var maxCloseness = 0;
3740 var nodes = this.nodes();
3741 var fw = this.floydWarshall({
3742 weight: weight,
3743 directed: directed
3744 }); // Compute closeness for every node and find the maximum closeness
3745
3746 for (var i = 0; i < nodes.length; i++) {
3747 var currCloseness = 0;
3748 var node_i = nodes[i];
3749
3750 for (var j = 0; j < nodes.length; j++) {
3751 if (i !== j) {
3752 var d = fw.distance(node_i, nodes[j]);
3753
3754 if (harmonic) {
3755 currCloseness += 1 / d;
3756 } else {
3757 currCloseness += d;
3758 }
3759 }
3760 }
3761
3762 if (!harmonic) {
3763 currCloseness = 1 / currCloseness;
3764 }
3765
3766 if (maxCloseness < currCloseness) {
3767 maxCloseness = currCloseness;
3768 }
3769
3770 closenesses[node_i.id()] = currCloseness;
3771 }
3772
3773 return {
3774 closeness: function closeness(node) {
3775 if (maxCloseness == 0) {
3776 return 0;
3777 }
3778
3779 if (string(node)) {
3780 // from is a selector string
3781 node = cy.filter(node)[0].id();
3782 } else {
3783 // from is a node
3784 node = node.id();
3785 }
3786
3787 return closenesses[node] / maxCloseness;
3788 }
3789 };
3790 },
3791 // Implemented from pseudocode from wikipedia
3792 closenessCentrality: function closenessCentrality(options) {
3793 var _defaults2 = defaults$2(options),
3794 root = _defaults2.root,
3795 weight = _defaults2.weight,
3796 directed = _defaults2.directed,
3797 harmonic = _defaults2.harmonic;
3798
3799 root = this.filter(root)[0]; // we need distance from this node to every other node
3800
3801 var dijkstra = this.dijkstra({
3802 root: root,
3803 weight: weight,
3804 directed: directed
3805 });
3806 var totalDistance = 0;
3807 var nodes = this.nodes();
3808
3809 for (var i = 0; i < nodes.length; i++) {
3810 var n = nodes[i];
3811
3812 if (!n.same(root)) {
3813 var d = dijkstra.distanceTo(n);
3814
3815 if (harmonic) {
3816 totalDistance += 1 / d;
3817 } else {
3818 totalDistance += d;
3819 }
3820 }
3821 }
3822
3823 return harmonic ? totalDistance : 1 / totalDistance;
3824 } // closenessCentrality
3825
3826}; // elesfn
3827// nice, short mathemathical alias
3828
3829elesfn$9.cc = elesfn$9.closenessCentrality;
3830elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
3831
3832var defaults$3 = defaults({
3833 weight: null,
3834 directed: false
3835});
3836var elesfn$a = {
3837 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
3838 betweennessCentrality: function betweennessCentrality(options) {
3839 var _defaults = defaults$3(options),
3840 directed = _defaults.directed,
3841 weight = _defaults.weight;
3842
3843 var weighted = weight != null;
3844 var cy = this.cy(); // starting
3845
3846 var V = this.nodes();
3847 var A = {};
3848 var _C = {};
3849 var max = 0;
3850 var C = {
3851 set: function set(key, val) {
3852 _C[key] = val;
3853
3854 if (val > max) {
3855 max = val;
3856 }
3857 },
3858 get: function get(key) {
3859 return _C[key];
3860 }
3861 }; // A contains the neighborhoods of every node
3862
3863 for (var i = 0; i < V.length; i++) {
3864 var v = V[i];
3865 var vid = v.id();
3866
3867 if (directed) {
3868 A[vid] = v.outgoers().nodes(); // get outgoers of every node
3869 } else {
3870 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
3871 }
3872
3873 C.set(vid, 0);
3874 }
3875
3876 var _loop = function _loop(s) {
3877 var sid = V[s].id();
3878 var S = []; // stack
3879
3880 var P = {};
3881 var g = {};
3882 var d = {};
3883 var Q = new Heap(function (a, b) {
3884 return d[a] - d[b];
3885 }); // queue
3886 // init dictionaries
3887
3888 for (var _i = 0; _i < V.length; _i++) {
3889 var _vid = V[_i].id();
3890
3891 P[_vid] = [];
3892 g[_vid] = 0;
3893 d[_vid] = Infinity;
3894 }
3895
3896 g[sid] = 1; // sigma
3897
3898 d[sid] = 0; // distance to s
3899
3900 Q.push(sid);
3901
3902 while (!Q.empty()) {
3903 var _v = Q.pop();
3904
3905 S.push(_v);
3906
3907 if (weighted) {
3908 for (var j = 0; j < A[_v].length; j++) {
3909 var w = A[_v][j];
3910 var vEle = cy.getElementById(_v);
3911 var edge = void 0;
3912
3913 if (vEle.edgesTo(w).length > 0) {
3914 edge = vEle.edgesTo(w)[0];
3915 } else {
3916 edge = w.edgesTo(vEle)[0];
3917 }
3918
3919 var edgeWeight = weight(edge);
3920 w = w.id();
3921
3922 if (d[w] > d[_v] + edgeWeight) {
3923 d[w] = d[_v] + edgeWeight;
3924
3925 if (Q.nodes.indexOf(w) < 0) {
3926 //if w is not in Q
3927 Q.push(w);
3928 } else {
3929 // update position if w is in Q
3930 Q.updateItem(w);
3931 }
3932
3933 g[w] = 0;
3934 P[w] = [];
3935 }
3936
3937 if (d[w] == d[_v] + edgeWeight) {
3938 g[w] = g[w] + g[_v];
3939 P[w].push(_v);
3940 }
3941 }
3942 } else {
3943 for (var _j = 0; _j < A[_v].length; _j++) {
3944 var _w = A[_v][_j].id();
3945
3946 if (d[_w] == Infinity) {
3947 Q.push(_w);
3948 d[_w] = d[_v] + 1;
3949 }
3950
3951 if (d[_w] == d[_v] + 1) {
3952 g[_w] = g[_w] + g[_v];
3953
3954 P[_w].push(_v);
3955 }
3956 }
3957 }
3958 }
3959
3960 var e = {};
3961
3962 for (var _i2 = 0; _i2 < V.length; _i2++) {
3963 e[V[_i2].id()] = 0;
3964 }
3965
3966 while (S.length > 0) {
3967 var _w2 = S.pop();
3968
3969 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
3970 var _v2 = P[_w2][_j2];
3971 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
3972 }
3973
3974 if (_w2 != V[s].id()) {
3975 C.set(_w2, C.get(_w2) + e[_w2]);
3976 }
3977 }
3978 };
3979
3980 for (var s = 0; s < V.length; s++) {
3981 _loop(s);
3982 }
3983
3984 var ret = {
3985 betweenness: function betweenness(node) {
3986 var id = cy.collection(node).id();
3987 return C.get(id);
3988 },
3989 betweennessNormalized: function betweennessNormalized(node) {
3990 if (max == 0) {
3991 return 0;
3992 }
3993
3994 var id = cy.collection(node).id();
3995 return C.get(id) / max;
3996 }
3997 }; // alias
3998
3999 ret.betweennessNormalised = ret.betweennessNormalized;
4000 return ret;
4001 } // betweennessCentrality
4002
4003}; // elesfn
4004// nice, short mathemathical alias
4005
4006elesfn$a.bc = elesfn$a.betweennessCentrality;
4007
4008// Implemented by Zoe Xi @zoexi for GSOC 2016
4009/* eslint-disable no-unused-vars */
4010
4011var defaults$4 = defaults({
4012 expandFactor: 2,
4013 // affects time of computation and cluster granularity to some extent: M * M
4014 inflateFactor: 2,
4015 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4016 multFactor: 1,
4017 // optional self loops for each node. Use a neutral value to improve cluster computations.
4018 maxIterations: 20,
4019 // maximum number of iterations of the MCL algorithm in a single run
4020 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4021 function (edge) {
4022 return 1;
4023 }]
4024});
4025/* eslint-enable */
4026
4027var setOptions = function setOptions(options) {
4028 return defaults$4(options);
4029};
4030/* eslint-enable */
4031
4032
4033var getSimilarity = function getSimilarity(edge, attributes) {
4034 var total = 0;
4035
4036 for (var i = 0; i < attributes.length; i++) {
4037 total += attributes[i](edge);
4038 }
4039
4040 return total;
4041};
4042
4043var addLoops = function addLoops(M, n, val) {
4044 for (var i = 0; i < n; i++) {
4045 M[i * n + i] = val;
4046 }
4047};
4048
4049var normalize = function normalize(M, n) {
4050 var sum;
4051
4052 for (var col = 0; col < n; col++) {
4053 sum = 0;
4054
4055 for (var row = 0; row < n; row++) {
4056 sum += M[row * n + col];
4057 }
4058
4059 for (var _row = 0; _row < n; _row++) {
4060 M[_row * n + col] = M[_row * n + col] / sum;
4061 }
4062 }
4063}; // TODO: blocked matrix multiplication?
4064
4065
4066var mmult = function mmult(A, B, n) {
4067 var C = new Array(n * n);
4068
4069 for (var i = 0; i < n; i++) {
4070 for (var j = 0; j < n; j++) {
4071 C[i * n + j] = 0;
4072 }
4073
4074 for (var k = 0; k < n; k++) {
4075 for (var _j = 0; _j < n; _j++) {
4076 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4077 }
4078 }
4079 }
4080
4081 return C;
4082};
4083
4084var expand = function expand(M, n, expandFactor
4085/** power **/
4086) {
4087 var _M = M.slice(0);
4088
4089 for (var p = 1; p < expandFactor; p++) {
4090 M = mmult(M, _M, n);
4091 }
4092
4093 return M;
4094};
4095
4096var inflate = function inflate(M, n, inflateFactor
4097/** r **/
4098) {
4099 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4100
4101
4102 for (var i = 0; i < n * n; i++) {
4103 _M[i] = Math.pow(M[i], inflateFactor);
4104 }
4105
4106 normalize(_M, n);
4107 return _M;
4108};
4109
4110var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4111 // Check that both matrices have the same elements (i,j)
4112 for (var i = 0; i < n2; i++) {
4113 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4114
4115 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4116
4117 if (v1 !== v2) {
4118 return false;
4119 }
4120 }
4121
4122 return true;
4123};
4124
4125var assign = function assign(M, n, nodes, cy) {
4126 var clusters = [];
4127
4128 for (var i = 0; i < n; i++) {
4129 var cluster = [];
4130
4131 for (var j = 0; j < n; j++) {
4132 // Row-wise attractors and elements that they attract belong in same cluster
4133 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4134 cluster.push(nodes[j]);
4135 }
4136 }
4137
4138 if (cluster.length !== 0) {
4139 clusters.push(cy.collection(cluster));
4140 }
4141 }
4142
4143 return clusters;
4144};
4145
4146var isDuplicate = function isDuplicate(c1, c2) {
4147 for (var i = 0; i < c1.length; i++) {
4148 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4149 return false;
4150 }
4151 }
4152
4153 return true;
4154};
4155
4156var removeDuplicates = function removeDuplicates(clusters) {
4157 for (var i = 0; i < clusters.length; i++) {
4158 for (var j = 0; j < clusters.length; j++) {
4159 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4160 clusters.splice(j, 1);
4161 }
4162 }
4163 }
4164
4165 return clusters;
4166};
4167
4168var markovClustering = function markovClustering(options) {
4169 var nodes = this.nodes();
4170 var edges = this.edges();
4171 var cy = this.cy(); // Set parameters of algorithm:
4172
4173 var opts = setOptions(options); // Map each node to its position in node array
4174
4175 var id2position = {};
4176
4177 for (var i = 0; i < nodes.length; i++) {
4178 id2position[nodes[i].id()] = i;
4179 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4180
4181
4182 var n = nodes.length,
4183 n2 = n * n;
4184
4185 var M = new Array(n2),
4186 _M;
4187
4188 for (var _i = 0; _i < n2; _i++) {
4189 M[_i] = 0;
4190 }
4191
4192 for (var e = 0; e < edges.length; e++) {
4193 var edge = edges[e];
4194 var _i2 = id2position[edge.source().id()];
4195 var j = id2position[edge.target().id()];
4196 var sim = getSimilarity(edge, opts.attributes);
4197 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4198
4199 M[j * n + _i2] += sim;
4200 } // Begin Markov cluster algorithm
4201 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4202
4203
4204 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4205
4206 normalize(M, n);
4207 var isStillMoving = true;
4208 var iterations = 0;
4209
4210 while (isStillMoving && iterations < opts.maxIterations) {
4211 isStillMoving = false; // Step 3:
4212
4213 _M = expand(M, n, opts.expandFactor); // Step 4:
4214
4215 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4216
4217 if (!hasConverged(M, _M, n2, 4)) {
4218 isStillMoving = true;
4219 }
4220
4221 iterations++;
4222 } // Build clusters from matrix
4223
4224
4225 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4226
4227 clusters = removeDuplicates(clusters);
4228 return clusters;
4229};
4230
4231var markovClustering$1 = {
4232 markovClustering: markovClustering,
4233 mcl: markovClustering
4234};
4235
4236// Common distance metrics for clustering algorithms
4237
4238var identity = function identity(x) {
4239 return x;
4240};
4241
4242var absDiff = function absDiff(p, q) {
4243 return Math.abs(q - p);
4244};
4245
4246var addAbsDiff = function addAbsDiff(total, p, q) {
4247 return total + absDiff(p, q);
4248};
4249
4250var addSquaredDiff = function addSquaredDiff(total, p, q) {
4251 return total + Math.pow(q - p, 2);
4252};
4253
4254var sqrt = function sqrt(x) {
4255 return Math.sqrt(x);
4256};
4257
4258var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4259 return Math.max(currentMax, absDiff(p, q));
4260};
4261
4262var getDistance = function getDistance(length, getP, getQ, init, visit) {
4263 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4264 var ret = init;
4265 var p, q;
4266
4267 for (var dim = 0; dim < length; dim++) {
4268 p = getP(dim);
4269 q = getQ(dim);
4270 ret = visit(ret, p, q);
4271 }
4272
4273 return post(ret);
4274};
4275
4276var distances = {
4277 euclidean: function euclidean(length, getP, getQ) {
4278 if (length >= 2) {
4279 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4280 } else {
4281 // for single attr case, more efficient to avoid sqrt
4282 return getDistance(length, getP, getQ, 0, addAbsDiff);
4283 }
4284 },
4285 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4286 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4287 },
4288 manhattan: function manhattan(length, getP, getQ) {
4289 return getDistance(length, getP, getQ, 0, addAbsDiff);
4290 },
4291 max: function max(length, getP, getQ) {
4292 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4293 }
4294}; // in case the user accidentally doesn't use camel case
4295
4296distances['squared-euclidean'] = distances['squaredEuclidean'];
4297distances['squaredeuclidean'] = distances['squaredEuclidean'];
4298function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4299 var impl;
4300
4301 if (fn(method)) {
4302 impl = method;
4303 } else {
4304 impl = distances[method] || distances.euclidean;
4305 }
4306
4307 if (length === 0 && fn(method)) {
4308 return impl(nodeP, nodeQ);
4309 } else {
4310 return impl(length, getP, getQ, nodeP, nodeQ);
4311 }
4312}
4313
4314var defaults$5 = defaults({
4315 k: 2,
4316 m: 2,
4317 sensitivityThreshold: 0.0001,
4318 distance: 'euclidean',
4319 maxIterations: 10,
4320 attributes: [],
4321 testMode: false,
4322 testCentroids: null
4323});
4324
4325var setOptions$1 = function setOptions(options) {
4326 return defaults$5(options);
4327};
4328/* eslint-enable */
4329
4330
4331var getDist = function getDist(type, node, centroid, attributes, mode) {
4332 var noNodeP = mode !== 'kMedoids';
4333 var getP = noNodeP ? function (i) {
4334 return centroid[i];
4335 } : function (i) {
4336 return attributes[i](centroid);
4337 };
4338
4339 var getQ = function getQ(i) {
4340 return attributes[i](node);
4341 };
4342
4343 var nodeP = centroid;
4344 var nodeQ = node;
4345 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4346};
4347
4348var randomCentroids = function randomCentroids(nodes, k, attributes) {
4349 var ndim = attributes.length;
4350 var min = new Array(ndim);
4351 var max = new Array(ndim);
4352 var centroids = new Array(k);
4353 var centroid = null; // Find min, max values for each attribute dimension
4354
4355 for (var i = 0; i < ndim; i++) {
4356 min[i] = nodes.min(attributes[i]).value;
4357 max[i] = nodes.max(attributes[i]).value;
4358 } // Build k centroids, each represented as an n-dim feature vector
4359
4360
4361 for (var c = 0; c < k; c++) {
4362 centroid = [];
4363
4364 for (var _i = 0; _i < ndim; _i++) {
4365 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4366 }
4367
4368 centroids[c] = centroid;
4369 }
4370
4371 return centroids;
4372};
4373
4374var classify = function classify(node, centroids, distance, attributes, type) {
4375 var min = Infinity;
4376 var index = 0;
4377
4378 for (var i = 0; i < centroids.length; i++) {
4379 var dist = getDist(distance, node, centroids[i], attributes, type);
4380
4381 if (dist < min) {
4382 min = dist;
4383 index = i;
4384 }
4385 }
4386
4387 return index;
4388};
4389
4390var buildCluster = function buildCluster(centroid, nodes, assignment) {
4391 var cluster = [];
4392 var node = null;
4393
4394 for (var n = 0; n < nodes.length; n++) {
4395 node = nodes[n];
4396
4397 if (assignment[node.id()] === centroid) {
4398 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4399 cluster.push(node);
4400 }
4401 }
4402
4403 return cluster;
4404};
4405
4406var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4407 return Math.abs(v2 - v1) <= sensitivityThreshold;
4408};
4409
4410var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4411 for (var i = 0; i < v1.length; i++) {
4412 for (var j = 0; j < v1[i].length; j++) {
4413 var diff = Math.abs(v1[i][j] - v2[i][j]);
4414
4415 if (diff > sensitivityThreshold) {
4416 return false;
4417 }
4418 }
4419 }
4420
4421 return true;
4422};
4423
4424var seenBefore = function seenBefore(node, medoids, n) {
4425 for (var i = 0; i < n; i++) {
4426 if (node === medoids[i]) return true;
4427 }
4428
4429 return false;
4430};
4431
4432var randomMedoids = function randomMedoids(nodes, k) {
4433 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
4434 // so we need to check to see if we've already seen or chose this node before.
4435
4436 if (nodes.length < 50) {
4437 // Randomly select k medoids from the n nodes
4438 for (var i = 0; i < k; i++) {
4439 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).
4440 // Instead choose a different random node.
4441
4442 while (seenBefore(node, medoids, i)) {
4443 node = nodes[Math.floor(Math.random() * nodes.length)];
4444 }
4445
4446 medoids[i] = node;
4447 }
4448 } else {
4449 // Relatively large data set, so pretty safe to not check and just select random nodes
4450 for (var _i2 = 0; _i2 < k; _i2++) {
4451 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4452 }
4453 }
4454
4455 return medoids;
4456};
4457
4458var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4459 var cost = 0;
4460
4461 for (var n = 0; n < cluster.length; n++) {
4462 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4463 }
4464
4465 return cost;
4466};
4467
4468var kMeans = function kMeans(options) {
4469 var cy = this.cy();
4470 var nodes = this.nodes();
4471 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
4472
4473 var opts = setOptions$1(options); // Begin k-means algorithm
4474
4475 var clusters = new Array(opts.k);
4476 var assignment = {};
4477 var centroids; // Step 1: Initialize centroid positions
4478
4479 if (opts.testMode) {
4480 if (typeof opts.testCentroids === 'number') {
4481 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4482 } else if (_typeof(opts.testCentroids) === 'object') {
4483 centroids = opts.testCentroids;
4484 } else {
4485 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4486 }
4487 } else {
4488 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4489 }
4490
4491 var isStillMoving = true;
4492 var iterations = 0;
4493
4494 while (isStillMoving && iterations < opts.maxIterations) {
4495 // Step 2: Assign nodes to the nearest centroid
4496 for (var n = 0; n < nodes.length; n++) {
4497 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4498
4499 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
4500 } // Step 3: For each of the k clusters, update its centroid
4501
4502
4503 isStillMoving = false;
4504
4505 for (var c = 0; c < opts.k; c++) {
4506 // Get all nodes that belong to this cluster
4507 var cluster = buildCluster(c, nodes, assignment);
4508
4509 if (cluster.length === 0) {
4510 // If cluster is empty, break out early & move to next cluster
4511 continue;
4512 } // Update centroids by calculating avg of all nodes within the cluster.
4513
4514
4515 var ndim = opts.attributes.length;
4516 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
4517
4518 var newCentroid = new Array(ndim);
4519 var sum = new Array(ndim);
4520
4521 for (var d = 0; d < ndim; d++) {
4522 sum[d] = 0.0;
4523
4524 for (var i = 0; i < cluster.length; i++) {
4525 node = cluster[i];
4526 sum[d] += opts.attributes[d](node);
4527 }
4528
4529 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
4530
4531 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
4532 isStillMoving = true;
4533 }
4534 }
4535
4536 centroids[c] = newCentroid;
4537 clusters[c] = cy.collection(cluster);
4538 }
4539
4540 iterations++;
4541 }
4542
4543 return clusters;
4544};
4545
4546var kMedoids = function kMedoids(options) {
4547 var cy = this.cy();
4548 var nodes = this.nodes();
4549 var node = null;
4550 var opts = setOptions$1(options); // Begin k-medoids algorithm
4551
4552 var clusters = new Array(opts.k);
4553 var medoids;
4554 var assignment = {};
4555 var curCost;
4556 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
4557 // Step 1: Initialize k medoids
4558
4559 if (opts.testMode) {
4560 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
4561 medoids = opts.testCentroids;
4562 } else {
4563 medoids = randomMedoids(nodes, opts.k);
4564 }
4565 } else {
4566 medoids = randomMedoids(nodes, opts.k);
4567 }
4568
4569 var isStillMoving = true;
4570 var iterations = 0;
4571
4572 while (isStillMoving && iterations < opts.maxIterations) {
4573 // Step 2: Assign nodes to the nearest medoid
4574 for (var n = 0; n < nodes.length; n++) {
4575 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4576
4577 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
4578 }
4579
4580 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
4581 // select the node with the lowest configuration cost as new medoid.
4582
4583 for (var m = 0; m < medoids.length; m++) {
4584 // Get all nodes that belong to this medoid
4585 var cluster = buildCluster(m, nodes, assignment);
4586
4587 if (cluster.length === 0) {
4588 // If cluster is empty, break out early & move to next cluster
4589 continue;
4590 }
4591
4592 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
4593 // Select different medoid if its configuration has the lowest cost
4594
4595 for (var _n = 0; _n < cluster.length; _n++) {
4596 curCost = findCost(cluster[_n], cluster, opts.attributes);
4597
4598 if (curCost < minCosts[m]) {
4599 minCosts[m] = curCost;
4600 medoids[m] = cluster[_n];
4601 isStillMoving = true;
4602 }
4603 }
4604
4605 clusters[m] = cy.collection(cluster);
4606 }
4607
4608 iterations++;
4609 }
4610
4611 return clusters;
4612};
4613
4614var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
4615 var numerator, denominator;
4616
4617 for (var n = 0; n < nodes.length; n++) {
4618 for (var c = 0; c < centroids.length; c++) {
4619 weight[n][c] = Math.pow(U[n][c], opts.m);
4620 }
4621 }
4622
4623 for (var _c = 0; _c < centroids.length; _c++) {
4624 for (var dim = 0; dim < opts.attributes.length; dim++) {
4625 numerator = 0;
4626 denominator = 0;
4627
4628 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
4629 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
4630 denominator += weight[_n2][_c];
4631 }
4632
4633 centroids[_c][dim] = numerator / denominator;
4634 }
4635 }
4636};
4637
4638var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
4639 // Save previous step
4640 for (var i = 0; i < U.length; i++) {
4641 _U[i] = U[i].slice();
4642 }
4643
4644 var sum, numerator, denominator;
4645 var pow = 2 / (opts.m - 1);
4646
4647 for (var c = 0; c < centroids.length; c++) {
4648 for (var n = 0; n < nodes.length; n++) {
4649 sum = 0;
4650
4651 for (var k = 0; k < centroids.length; k++) {
4652 // against all other centroids
4653 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
4654 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
4655 sum += Math.pow(numerator / denominator, pow);
4656 }
4657
4658 U[n][c] = 1 / sum;
4659 }
4660 }
4661};
4662
4663var assign$1 = function assign(nodes, U, opts, cy) {
4664 var clusters = new Array(opts.k);
4665
4666 for (var c = 0; c < clusters.length; c++) {
4667 clusters[c] = [];
4668 }
4669
4670 var max;
4671 var index;
4672
4673 for (var n = 0; n < U.length; n++) {
4674 // for each node (U is N x C matrix)
4675 max = -Infinity;
4676 index = -1; // Determine which cluster the node is most likely to belong in
4677
4678 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
4679 if (U[n][_c2] > max) {
4680 max = U[n][_c2];
4681 index = _c2;
4682 }
4683 }
4684
4685 clusters[index].push(nodes[n]);
4686 } // Turn every array into a collection of nodes
4687
4688
4689 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
4690 clusters[_c3] = cy.collection(clusters[_c3]);
4691 }
4692
4693 return clusters;
4694};
4695
4696var fuzzyCMeans = function fuzzyCMeans(options) {
4697 var cy = this.cy();
4698 var nodes = this.nodes();
4699 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
4700
4701 var clusters;
4702 var centroids;
4703 var U;
4704
4705 var _U;
4706
4707 var weight; // Step 1: Initialize letiables.
4708
4709 _U = new Array(nodes.length);
4710
4711 for (var i = 0; i < nodes.length; i++) {
4712 // N x C matrix
4713 _U[i] = new Array(opts.k);
4714 }
4715
4716 U = new Array(nodes.length);
4717
4718 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
4719 // N x C matrix
4720 U[_i3] = new Array(opts.k);
4721 }
4722
4723 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
4724 var total = 0;
4725
4726 for (var j = 0; j < opts.k; j++) {
4727 U[_i4][j] = Math.random();
4728 total += U[_i4][j];
4729 }
4730
4731 for (var _j = 0; _j < opts.k; _j++) {
4732 U[_i4][_j] = U[_i4][_j] / total;
4733 }
4734 }
4735
4736 centroids = new Array(opts.k);
4737
4738 for (var _i5 = 0; _i5 < opts.k; _i5++) {
4739 centroids[_i5] = new Array(opts.attributes.length);
4740 }
4741
4742 weight = new Array(nodes.length);
4743
4744 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
4745 // N x C matrix
4746 weight[_i6] = new Array(opts.k);
4747 } // end init FCM
4748
4749
4750 var isStillMoving = true;
4751 var iterations = 0;
4752
4753 while (isStillMoving && iterations < opts.maxIterations) {
4754 isStillMoving = false; // Step 2: Calculate the centroids for each step.
4755
4756 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
4757
4758 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
4759
4760 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
4761 isStillMoving = true;
4762 }
4763
4764 iterations++;
4765 } // Assign nodes to clusters with highest probability.
4766
4767
4768 clusters = assign$1(nodes, U, opts, cy);
4769 return {
4770 clusters: clusters,
4771 degreeOfMembership: U
4772 };
4773};
4774
4775var kClustering = {
4776 kMeans: kMeans,
4777 kMedoids: kMedoids,
4778 fuzzyCMeans: fuzzyCMeans,
4779 fcm: fuzzyCMeans
4780};
4781
4782// Implemented by Zoe Xi @zoexi for GSOC 2016
4783var defaults$6 = defaults({
4784 distance: 'euclidean',
4785 // distance metric to compare nodes
4786 linkage: 'min',
4787 // linkage criterion : how to determine the distance between clusters of nodes
4788 mode: 'threshold',
4789 // mode:'threshold' => clusters must be threshold distance apart
4790 threshold: Infinity,
4791 // the distance threshold
4792 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
4793 addDendrogram: false,
4794 // whether to add the dendrogram to the graph for viz
4795 dendrogramDepth: 0,
4796 // depth at which dendrogram branches are merged into the returned clusters
4797 attributes: [] // array of attr functions
4798
4799});
4800var linkageAliases = {
4801 'single': 'min',
4802 'complete': 'max'
4803};
4804
4805var setOptions$2 = function setOptions(options) {
4806 var opts = defaults$6(options);
4807 var preferredAlias = linkageAliases[opts.linkage];
4808
4809 if (preferredAlias != null) {
4810 opts.linkage = preferredAlias;
4811 }
4812
4813 return opts;
4814};
4815
4816var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
4817 // Find two closest clusters from cached mins
4818 var minKey = 0;
4819 var min = Infinity;
4820 var dist;
4821 var attrs = opts.attributes;
4822
4823 var getDist = function getDist(n1, n2) {
4824 return clusteringDistance(opts.distance, attrs.length, function (i) {
4825 return attrs[i](n1);
4826 }, function (i) {
4827 return attrs[i](n2);
4828 }, n1, n2);
4829 };
4830
4831 for (var i = 0; i < clusters.length; i++) {
4832 var key = clusters[i].key;
4833 var _dist = dists[key][mins[key]];
4834
4835 if (_dist < min) {
4836 minKey = key;
4837 min = _dist;
4838 }
4839 }
4840
4841 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
4842 return false;
4843 }
4844
4845 var c1 = index[minKey];
4846 var c2 = index[mins[minKey]];
4847 var merged; // Merge two closest clusters
4848
4849 if (opts.mode === 'dendrogram') {
4850 merged = {
4851 left: c1,
4852 right: c2,
4853 key: c1.key
4854 };
4855 } else {
4856 merged = {
4857 value: c1.value.concat(c2.value),
4858 key: c1.key
4859 };
4860 }
4861
4862 clusters[c1.index] = merged;
4863 clusters.splice(c2.index, 1);
4864 index[c1.key] = merged; // Update distances with new merged cluster
4865
4866 for (var _i = 0; _i < clusters.length; _i++) {
4867 var cur = clusters[_i];
4868
4869 if (c1.key === cur.key) {
4870 dist = Infinity;
4871 } else if (opts.linkage === 'min') {
4872 dist = dists[c1.key][cur.key];
4873
4874 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
4875 dist = dists[c2.key][cur.key];
4876 }
4877 } else if (opts.linkage === 'max') {
4878 dist = dists[c1.key][cur.key];
4879
4880 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
4881 dist = dists[c2.key][cur.key];
4882 }
4883 } else if (opts.linkage === 'mean') {
4884 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
4885 } else {
4886 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
4887 }
4888
4889 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
4890 } // Update cached mins
4891
4892
4893 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
4894 var key1 = clusters[_i2].key;
4895
4896 if (mins[key1] === c1.key || mins[key1] === c2.key) {
4897 var _min = key1;
4898
4899 for (var j = 0; j < clusters.length; j++) {
4900 var key2 = clusters[j].key;
4901
4902 if (dists[key1][key2] < dists[key1][_min]) {
4903 _min = key2;
4904 }
4905 }
4906
4907 mins[key1] = _min;
4908 }
4909
4910 clusters[_i2].index = _i2;
4911 } // Clean up meta data used for clustering
4912
4913
4914 c1.key = c2.key = c1.index = c2.index = null;
4915 return true;
4916};
4917
4918var getAllChildren = function getAllChildren(root, arr, cy) {
4919 if (!root) return;
4920
4921 if (root.value) {
4922 arr.push(root.value);
4923 } else {
4924 if (root.left) getAllChildren(root.left, arr);
4925 if (root.right) getAllChildren(root.right, arr);
4926 }
4927};
4928
4929var buildDendrogram = function buildDendrogram(root, cy) {
4930 if (!root) return '';
4931
4932 if (root.left && root.right) {
4933 var leftStr = buildDendrogram(root.left, cy);
4934 var rightStr = buildDendrogram(root.right, cy);
4935 var node = cy.add({
4936 group: 'nodes',
4937 data: {
4938 id: leftStr + ',' + rightStr
4939 }
4940 });
4941 cy.add({
4942 group: 'edges',
4943 data: {
4944 source: leftStr,
4945 target: node.id()
4946 }
4947 });
4948 cy.add({
4949 group: 'edges',
4950 data: {
4951 source: rightStr,
4952 target: node.id()
4953 }
4954 });
4955 return node.id();
4956 } else if (root.value) {
4957 return root.value.id();
4958 }
4959};
4960
4961var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
4962 if (!root) return [];
4963 var left = [],
4964 right = [],
4965 leaves = [];
4966
4967 if (k === 0) {
4968 // don't cut tree, simply return all nodes as 1 single cluster
4969 if (root.left) getAllChildren(root.left, left);
4970 if (root.right) getAllChildren(root.right, right);
4971 leaves = left.concat(right);
4972 return [cy.collection(leaves)];
4973 } else if (k === 1) {
4974 // cut at root
4975 if (root.value) {
4976 // leaf node
4977 return [cy.collection(root.value)];
4978 } else {
4979 if (root.left) getAllChildren(root.left, left);
4980 if (root.right) getAllChildren(root.right, right);
4981 return [cy.collection(left), cy.collection(right)];
4982 }
4983 } else {
4984 if (root.value) {
4985 return [cy.collection(root.value)];
4986 } else {
4987 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
4988 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
4989 return left.concat(right);
4990 }
4991 }
4992};
4993/* eslint-enable */
4994
4995
4996var hierarchicalClustering = function hierarchicalClustering(options) {
4997 var cy = this.cy();
4998 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
4999
5000 var opts = setOptions$2(options);
5001 var attrs = opts.attributes;
5002
5003 var getDist = function getDist(n1, n2) {
5004 return clusteringDistance(opts.distance, attrs.length, function (i) {
5005 return attrs[i](n1);
5006 }, function (i) {
5007 return attrs[i](n2);
5008 }, n1, n2);
5009 }; // Begin hierarchical algorithm
5010
5011
5012 var clusters = [];
5013 var dists = []; // distances between each pair of clusters
5014
5015 var mins = []; // closest cluster for each cluster
5016
5017 var index = []; // hash of all clusters by key
5018 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5019
5020 for (var n = 0; n < nodes.length; n++) {
5021 var cluster = {
5022 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5023 key: n,
5024 index: n
5025 };
5026 clusters[n] = cluster;
5027 index[n] = cluster;
5028 dists[n] = [];
5029 mins[n] = 0;
5030 } // Calculate the distance between each pair of clusters
5031
5032
5033 for (var i = 0; i < clusters.length; i++) {
5034 for (var j = 0; j <= i; j++) {
5035 var dist = void 0;
5036
5037 if (opts.mode === 'dendrogram') {
5038 // modes store cluster values differently
5039 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5040 } else {
5041 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5042 }
5043
5044 dists[i][j] = dist;
5045 dists[j][i] = dist;
5046
5047 if (dist < dists[i][mins[i]]) {
5048 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5049 }
5050 }
5051 } // Find the closest pair of clusters and merge them into a single cluster.
5052 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5053
5054
5055 var merged = mergeClosest(clusters, index, dists, mins, opts);
5056
5057 while (merged) {
5058 merged = mergeClosest(clusters, index, dists, mins, opts);
5059 }
5060
5061 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5062 // in addition to returning the clusters.
5063
5064 if (opts.mode === 'dendrogram') {
5065 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5066 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5067 } else {
5068 // Regular mode simply returns the clusters
5069 retClusters = new Array(clusters.length);
5070 clusters.forEach(function (cluster, i) {
5071 // Clean up meta data used for clustering
5072 cluster.key = cluster.index = null;
5073 retClusters[i] = cy.collection(cluster.value);
5074 });
5075 }
5076
5077 return retClusters;
5078};
5079
5080var hierarchicalClustering$1 = {
5081 hierarchicalClustering: hierarchicalClustering,
5082 hca: hierarchicalClustering
5083};
5084
5085// Implemented by Zoe Xi @zoexi for GSOC 2016
5086var defaults$7 = defaults({
5087 distance: 'euclidean',
5088 // distance metric to compare attributes between two nodes
5089 preference: 'median',
5090 // suitability of a data point to serve as an exemplar
5091 damping: 0.8,
5092 // damping factor between [0.5, 1)
5093 maxIterations: 1000,
5094 // max number of iterations to run
5095 minIterations: 100,
5096 // min number of iterations to run in order for clustering to stop
5097 attributes: [// functions to quantify the similarity between any two points
5098 // e.g. node => node.data('weight')
5099 ]
5100});
5101
5102var setOptions$3 = function setOptions(options) {
5103 var dmp = options.damping;
5104 var pref = options.preference;
5105
5106 if (!(0.5 <= dmp && dmp < 1)) {
5107 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5108 }
5109
5110 var validPrefs = ['median', 'mean', 'min', 'max'];
5111
5112 if (!(validPrefs.some(function (v) {
5113 return v === pref;
5114 }) || number(pref))) {
5115 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5116 return "'".concat(p, "'");
5117 }).join(', '), "] or a number. Got: ").concat(pref));
5118 }
5119
5120 return defaults$7(options);
5121};
5122/* eslint-enable */
5123
5124
5125var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5126 var attr = function attr(n, i) {
5127 return attributes[i](n);
5128 }; // nb negative because similarity should have an inverse relationship to distance
5129
5130
5131 return -clusteringDistance(type, attributes.length, function (i) {
5132 return attr(n1, i);
5133 }, function (i) {
5134 return attr(n2, i);
5135 }, n1, n2);
5136};
5137
5138var getPreference = function getPreference(S, preference) {
5139 // larger preference = greater # of clusters
5140 var p = null;
5141
5142 if (preference === 'median') {
5143 p = median(S);
5144 } else if (preference === 'mean') {
5145 p = mean(S);
5146 } else if (preference === 'min') {
5147 p = min(S);
5148 } else if (preference === 'max') {
5149 p = max(S);
5150 } else {
5151 // Custom preference number, as set by user
5152 p = preference;
5153 }
5154
5155 return p;
5156};
5157
5158var findExemplars = function findExemplars(n, R, A) {
5159 var indices = [];
5160
5161 for (var i = 0; i < n; i++) {
5162 if (R[i * n + i] + A[i * n + i] > 0) {
5163 indices.push(i);
5164 }
5165 }
5166
5167 return indices;
5168};
5169
5170var assignClusters = function assignClusters(n, S, exemplars) {
5171 var clusters = [];
5172
5173 for (var i = 0; i < n; i++) {
5174 var index = -1;
5175 var max = -Infinity;
5176
5177 for (var ei = 0; ei < exemplars.length; ei++) {
5178 var e = exemplars[ei];
5179
5180 if (S[i * n + e] > max) {
5181 index = e;
5182 max = S[i * n + e];
5183 }
5184 }
5185
5186 if (index > 0) {
5187 clusters.push(index);
5188 }
5189 }
5190
5191 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5192 clusters[exemplars[_ei]] = exemplars[_ei];
5193 }
5194
5195 return clusters;
5196};
5197
5198var assign$2 = function assign(n, S, exemplars) {
5199 var clusters = assignClusters(n, S, exemplars);
5200
5201 for (var ei = 0; ei < exemplars.length; ei++) {
5202 var ii = [];
5203
5204 for (var c = 0; c < clusters.length; c++) {
5205 if (clusters[c] === exemplars[ei]) {
5206 ii.push(c);
5207 }
5208 }
5209
5210 var maxI = -1;
5211 var maxSum = -Infinity;
5212
5213 for (var i = 0; i < ii.length; i++) {
5214 var sum = 0;
5215
5216 for (var j = 0; j < ii.length; j++) {
5217 sum += S[ii[j] * n + ii[i]];
5218 }
5219
5220 if (sum > maxSum) {
5221 maxI = i;
5222 maxSum = sum;
5223 }
5224 }
5225
5226 exemplars[ei] = ii[maxI];
5227 }
5228
5229 clusters = assignClusters(n, S, exemplars);
5230 return clusters;
5231};
5232
5233var affinityPropagation = function affinityPropagation(options) {
5234 var cy = this.cy();
5235 var nodes = this.nodes();
5236 var opts = setOptions$3(options); // Map each node to its position in node array
5237
5238 var id2position = {};
5239
5240 for (var i = 0; i < nodes.length; i++) {
5241 id2position[nodes[i].id()] = i;
5242 } // Begin affinity propagation algorithm
5243
5244
5245 var n; // number of data points
5246
5247 var n2; // size of matrices
5248
5249 var S; // similarity matrix (1D array)
5250
5251 var p; // preference/suitability of a data point to serve as an exemplar
5252
5253 var R; // responsibility matrix (1D array)
5254
5255 var A; // availability matrix (1D array)
5256
5257 n = nodes.length;
5258 n2 = n * n; // Initialize and build S similarity matrix
5259
5260 S = new Array(n2);
5261
5262 for (var _i = 0; _i < n2; _i++) {
5263 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5264 }
5265
5266 for (var _i2 = 0; _i2 < n; _i2++) {
5267 for (var j = 0; j < n; j++) {
5268 if (_i2 !== j) {
5269 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5270 }
5271 }
5272 } // Place preferences on the diagonal of S
5273
5274
5275 p = getPreference(S, opts.preference);
5276
5277 for (var _i3 = 0; _i3 < n; _i3++) {
5278 S[_i3 * n + _i3] = p;
5279 } // Initialize R responsibility matrix
5280
5281
5282 R = new Array(n2);
5283
5284 for (var _i4 = 0; _i4 < n2; _i4++) {
5285 R[_i4] = 0.0;
5286 } // Initialize A availability matrix
5287
5288
5289 A = new Array(n2);
5290
5291 for (var _i5 = 0; _i5 < n2; _i5++) {
5292 A[_i5] = 0.0;
5293 }
5294
5295 var old = new Array(n);
5296 var Rp = new Array(n);
5297 var se = new Array(n);
5298
5299 for (var _i6 = 0; _i6 < n; _i6++) {
5300 old[_i6] = 0.0;
5301 Rp[_i6] = 0.0;
5302 se[_i6] = 0;
5303 }
5304
5305 var e = new Array(n * opts.minIterations);
5306
5307 for (var _i7 = 0; _i7 < e.length; _i7++) {
5308 e[_i7] = 0;
5309 }
5310
5311 var iter;
5312
5313 for (iter = 0; iter < opts.maxIterations; iter++) {
5314 // main algorithmic loop
5315 // Update R responsibility matrix
5316 for (var _i8 = 0; _i8 < n; _i8++) {
5317 var max = -Infinity,
5318 max2 = -Infinity,
5319 maxI = -1,
5320 AS = 0.0;
5321
5322 for (var _j = 0; _j < n; _j++) {
5323 old[_j] = R[_i8 * n + _j];
5324 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5325
5326 if (AS >= max) {
5327 max2 = max;
5328 max = AS;
5329 maxI = _j;
5330 } else if (AS > max2) {
5331 max2 = AS;
5332 }
5333 }
5334
5335 for (var _j2 = 0; _j2 < n; _j2++) {
5336 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5337 }
5338
5339 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5340 } // Update A availability matrix
5341
5342
5343 for (var _i9 = 0; _i9 < n; _i9++) {
5344 var sum = 0;
5345
5346 for (var _j3 = 0; _j3 < n; _j3++) {
5347 old[_j3] = A[_j3 * n + _i9];
5348 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5349 sum += Rp[_j3];
5350 }
5351
5352 sum -= Rp[_i9];
5353 Rp[_i9] = R[_i9 * n + _i9];
5354 sum += Rp[_i9];
5355
5356 for (var _j4 = 0; _j4 < n; _j4++) {
5357 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5358 }
5359
5360 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5361 } // Check for convergence
5362
5363
5364 var K = 0;
5365
5366 for (var _i10 = 0; _i10 < n; _i10++) {
5367 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5368 e[iter % opts.minIterations * n + _i10] = E;
5369 K += E;
5370 }
5371
5372 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5373 var _sum = 0;
5374
5375 for (var _i11 = 0; _i11 < n; _i11++) {
5376 se[_i11] = 0;
5377
5378 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5379 se[_i11] += e[_j5 * n + _i11];
5380 }
5381
5382 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5383 _sum++;
5384 }
5385 }
5386
5387 if (_sum === n) {
5388 // then we have convergence
5389 break;
5390 }
5391 }
5392 } // Identify exemplars (cluster centers)
5393
5394
5395 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
5396
5397 var clusterIndices = assign$2(n, S, exemplarsIndices);
5398 var clusters = {};
5399
5400 for (var c = 0; c < exemplarsIndices.length; c++) {
5401 clusters[exemplarsIndices[c]] = [];
5402 }
5403
5404 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5405 var pos = id2position[nodes[_i12].id()];
5406
5407 var clusterIndex = clusterIndices[pos];
5408
5409 if (clusterIndex != null) {
5410 // the node may have not been assigned a cluster if no valid attributes were specified
5411 clusters[clusterIndex].push(nodes[_i12]);
5412 }
5413 }
5414
5415 var retClusters = new Array(exemplarsIndices.length);
5416
5417 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5418 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5419 }
5420
5421 return retClusters;
5422};
5423
5424var affinityPropagation$1 = {
5425 affinityPropagation: affinityPropagation,
5426 ap: affinityPropagation
5427};
5428
5429var hierholzerDefaults = defaults({
5430 root: undefined,
5431 directed: false
5432});
5433var elesfn$b = {
5434 hierholzer: function hierholzer(options) {
5435 if (!plainObject(options)) {
5436 var args = arguments;
5437 options = {
5438 root: args[0],
5439 directed: args[1]
5440 };
5441 }
5442
5443 var _hierholzerDefaults = hierholzerDefaults(options),
5444 root = _hierholzerDefaults.root,
5445 directed = _hierholzerDefaults.directed;
5446
5447 var eles = this;
5448 var dflag = false;
5449 var oddIn;
5450 var oddOut;
5451 var startVertex;
5452 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5453 var nodes = {};
5454 var edges = {};
5455
5456 if (directed) {
5457 eles.forEach(function (ele) {
5458 var id = ele.id();
5459
5460 if (ele.isNode()) {
5461 var ind = ele.indegree(true);
5462 var outd = ele.outdegree(true);
5463 var d1 = ind - outd;
5464 var d2 = outd - ind;
5465
5466 if (d1 == 1) {
5467 if (oddIn) dflag = true;else oddIn = id;
5468 } else if (d2 == 1) {
5469 if (oddOut) dflag = true;else oddOut = id;
5470 } else if (d2 > 1 || d1 > 1) {
5471 dflag = true;
5472 }
5473
5474 nodes[id] = [];
5475 ele.outgoers().forEach(function (e) {
5476 if (e.isEdge()) nodes[id].push(e.id());
5477 });
5478 } else {
5479 edges[id] = [undefined, ele.target().id()];
5480 }
5481 });
5482 } else {
5483 eles.forEach(function (ele) {
5484 var id = ele.id();
5485
5486 if (ele.isNode()) {
5487 var d = ele.degree(true);
5488
5489 if (d % 2) {
5490 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5491 }
5492
5493 nodes[id] = [];
5494 ele.connectedEdges().forEach(function (e) {
5495 return nodes[id].push(e.id());
5496 });
5497 } else {
5498 edges[id] = [ele.source().id(), ele.target().id()];
5499 }
5500 });
5501 }
5502
5503 var result = {
5504 found: false,
5505 trail: undefined
5506 };
5507 if (dflag) return result;else if (oddOut && oddIn) {
5508 if (directed) {
5509 if (startVertex && oddOut != startVertex) {
5510 return result;
5511 }
5512
5513 startVertex = oddOut;
5514 } else {
5515 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5516 return result;
5517 } else if (!startVertex) {
5518 startVertex = oddOut;
5519 }
5520 }
5521 } else {
5522 if (!startVertex) startVertex = eles[0].id();
5523 }
5524
5525 var walk = function walk(v) {
5526 var currentNode = v;
5527 var subtour = [v];
5528 var adj, adjTail, adjHead;
5529
5530 while (nodes[currentNode].length) {
5531 adj = nodes[currentNode].shift();
5532 adjTail = edges[adj][0];
5533 adjHead = edges[adj][1];
5534
5535 if (currentNode != adjHead) {
5536 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5537 return e != adj;
5538 });
5539 currentNode = adjHead;
5540 } else if (!directed && currentNode != adjTail) {
5541 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5542 return e != adj;
5543 });
5544 currentNode = adjTail;
5545 }
5546
5547 subtour.unshift(adj);
5548 subtour.unshift(currentNode);
5549 }
5550
5551 return subtour;
5552 };
5553
5554 var trail = [];
5555 var subtour = [];
5556 subtour = walk(startVertex);
5557
5558 while (subtour.length != 1) {
5559 if (nodes[subtour[0]].length == 0) {
5560 trail.unshift(eles.getElementById(subtour.shift()));
5561 trail.unshift(eles.getElementById(subtour.shift()));
5562 } else {
5563 subtour = walk(subtour.shift()).concat(subtour);
5564 }
5565 }
5566
5567 trail.unshift(eles.getElementById(subtour.shift())); // final node
5568
5569 for (var d in nodes) {
5570 if (nodes[d].length) {
5571 return result;
5572 }
5573 }
5574
5575 result.found = true;
5576 result.trail = this.spawn(trail, true);
5577 return result;
5578 }
5579};
5580
5581var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5582 var eles = this;
5583 var nodes = {};
5584 var id = 0;
5585 var edgeCount = 0;
5586 var components = [];
5587 var stack = [];
5588 var visitedEdges = {};
5589
5590 var buildComponent = function buildComponent(x, y) {
5591 var i = stack.length - 1;
5592 var cutset = [];
5593 var component = eles.spawn();
5594
5595 while (stack[i].x != x || stack[i].y != y) {
5596 cutset.push(stack.pop().edge);
5597 i--;
5598 }
5599
5600 cutset.push(stack.pop().edge);
5601 cutset.forEach(function (edge) {
5602 var connectedNodes = edge.connectedNodes().intersection(eles);
5603 component.merge(edge);
5604 connectedNodes.forEach(function (node) {
5605 var nodeId = node.id();
5606 var connectedEdges = node.connectedEdges().intersection(eles);
5607 component.merge(node);
5608
5609 if (!nodes[nodeId].cutVertex) {
5610 component.merge(connectedEdges);
5611 } else {
5612 component.merge(connectedEdges.filter(function (edge) {
5613 return edge.isLoop();
5614 }));
5615 }
5616 });
5617 });
5618 components.push(component);
5619 };
5620
5621 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5622 if (root === parent) edgeCount += 1;
5623 nodes[currentNode] = {
5624 id: id,
5625 low: id++,
5626 cutVertex: false
5627 };
5628 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5629
5630 if (edges.size() === 0) {
5631 components.push(eles.spawn(eles.getElementById(currentNode)));
5632 } else {
5633 var sourceId, targetId, otherNodeId, edgeId;
5634 edges.forEach(function (edge) {
5635 sourceId = edge.source().id();
5636 targetId = edge.target().id();
5637 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5638
5639 if (otherNodeId !== parent) {
5640 edgeId = edge.id();
5641
5642 if (!visitedEdges[edgeId]) {
5643 visitedEdges[edgeId] = true;
5644 stack.push({
5645 x: currentNode,
5646 y: otherNodeId,
5647 edge: edge
5648 });
5649 }
5650
5651 if (!(otherNodeId in nodes)) {
5652 biconnectedSearch(root, otherNodeId, currentNode);
5653 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
5654
5655 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
5656 nodes[currentNode].cutVertex = true;
5657 buildComponent(currentNode, otherNodeId);
5658 }
5659 } else {
5660 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
5661 }
5662 }
5663 });
5664 }
5665 };
5666
5667 eles.forEach(function (ele) {
5668 if (ele.isNode()) {
5669 var nodeId = ele.id();
5670
5671 if (!(nodeId in nodes)) {
5672 edgeCount = 0;
5673 biconnectedSearch(nodeId, nodeId);
5674 nodes[nodeId].cutVertex = edgeCount > 1;
5675 }
5676 }
5677 });
5678 var cutVertices = Object.keys(nodes).filter(function (id) {
5679 return nodes[id].cutVertex;
5680 }).map(function (id) {
5681 return eles.getElementById(id);
5682 });
5683 return {
5684 cut: eles.spawn(cutVertices),
5685 components: components
5686 };
5687};
5688
5689var hopcroftTarjanBiconnected$1 = {
5690 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
5691 htbc: hopcroftTarjanBiconnected,
5692 htb: hopcroftTarjanBiconnected,
5693 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
5694};
5695
5696var tarjanStronglyConnected = function tarjanStronglyConnected() {
5697 var eles = this;
5698 var nodes = {};
5699 var index = 0;
5700 var components = [];
5701 var stack = [];
5702 var cut = eles.spawn(eles);
5703
5704 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
5705 stack.push(sourceNodeId);
5706 nodes[sourceNodeId] = {
5707 index: index,
5708 low: index++,
5709 explored: false
5710 };
5711 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
5712 connectedEdges.forEach(function (edge) {
5713 var targetNodeId = edge.target().id();
5714
5715 if (targetNodeId !== sourceNodeId) {
5716 if (!(targetNodeId in nodes)) {
5717 stronglyConnectedSearch(targetNodeId);
5718 }
5719
5720 if (!nodes[targetNodeId].explored) {
5721 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
5722 }
5723 }
5724 });
5725
5726 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
5727 var componentNodes = eles.spawn();
5728
5729 for (;;) {
5730 var nodeId = stack.pop();
5731 componentNodes.merge(eles.getElementById(nodeId));
5732 nodes[nodeId].low = nodes[sourceNodeId].index;
5733 nodes[nodeId].explored = true;
5734
5735 if (nodeId === sourceNodeId) {
5736 break;
5737 }
5738 }
5739
5740 var componentEdges = componentNodes.edgesWith(componentNodes);
5741 var component = componentNodes.merge(componentEdges);
5742 components.push(component);
5743 cut = cut.difference(component);
5744 }
5745 };
5746
5747 eles.forEach(function (ele) {
5748 if (ele.isNode()) {
5749 var nodeId = ele.id();
5750
5751 if (!(nodeId in nodes)) {
5752 stronglyConnectedSearch(nodeId);
5753 }
5754 }
5755 });
5756 return {
5757 cut: cut,
5758 components: components
5759 };
5760};
5761
5762var tarjanStronglyConnected$1 = {
5763 tarjanStronglyConnected: tarjanStronglyConnected,
5764 tsc: tarjanStronglyConnected,
5765 tscc: tarjanStronglyConnected,
5766 tarjanStronglyConnectedComponents: tarjanStronglyConnected
5767};
5768
5769var elesfn$c = {};
5770[elesfn, elesfn$1, elesfn$2, elesfn$3, elesfn$4, elesfn$5, elesfn$6, elesfn$7, elesfn$8, elesfn$9, elesfn$a, markovClustering$1, kClustering, hierarchicalClustering$1, affinityPropagation$1, elesfn$b, hopcroftTarjanBiconnected$1, tarjanStronglyConnected$1].forEach(function (props) {
5771 extend(elesfn$c, props);
5772});
5773
5774/*!
5775Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
5776Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
5777Licensed under The MIT License (http://opensource.org/licenses/MIT)
5778*/
5779
5780/* promise states [Promises/A+ 2.1] */
5781var STATE_PENDING = 0;
5782/* [Promises/A+ 2.1.1] */
5783
5784var STATE_FULFILLED = 1;
5785/* [Promises/A+ 2.1.2] */
5786
5787var STATE_REJECTED = 2;
5788/* [Promises/A+ 2.1.3] */
5789
5790/* promise object constructor */
5791
5792var api = function api(executor) {
5793 /* optionally support non-constructor/plain-function call */
5794 if (!(this instanceof api)) return new api(executor);
5795 /* initialize object */
5796
5797 this.id = 'Thenable/1.0.7';
5798 this.state = STATE_PENDING;
5799 /* initial state */
5800
5801 this.fulfillValue = undefined;
5802 /* initial value */
5803
5804 /* [Promises/A+ 1.3, 2.1.2.2] */
5805
5806 this.rejectReason = undefined;
5807 /* initial reason */
5808
5809 /* [Promises/A+ 1.5, 2.1.3.2] */
5810
5811 this.onFulfilled = [];
5812 /* initial handlers */
5813
5814 this.onRejected = [];
5815 /* initial handlers */
5816
5817 /* provide optional information-hiding proxy */
5818
5819 this.proxy = {
5820 then: this.then.bind(this)
5821 };
5822 /* support optional executor function */
5823
5824 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
5825};
5826/* promise API methods */
5827
5828
5829api.prototype = {
5830 /* promise resolving methods */
5831 fulfill: function fulfill(value) {
5832 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
5833 },
5834 reject: function reject(value) {
5835 return deliver(this, STATE_REJECTED, 'rejectReason', value);
5836 },
5837
5838 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
5839 then: function then(onFulfilled, onRejected) {
5840 var curr = this;
5841 var next = new api();
5842 /* [Promises/A+ 2.2.7] */
5843
5844 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
5845 /* [Promises/A+ 2.2.2/2.2.6] */
5846
5847 curr.onRejected.push(resolver(onRejected, next, 'reject'));
5848 /* [Promises/A+ 2.2.3/2.2.6] */
5849
5850 execute(curr);
5851 return next.proxy;
5852 /* [Promises/A+ 2.2.7, 3.3] */
5853 }
5854};
5855/* deliver an action */
5856
5857var deliver = function deliver(curr, state, name, value) {
5858 if (curr.state === STATE_PENDING) {
5859 curr.state = state;
5860 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
5861
5862 curr[name] = value;
5863 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
5864
5865 execute(curr);
5866 }
5867
5868 return curr;
5869};
5870/* execute all handlers */
5871
5872
5873var execute = function execute(curr) {
5874 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
5875};
5876/* execute particular set of handlers */
5877
5878
5879var execute_handlers = function execute_handlers(curr, name, value) {
5880 /* global setImmediate: true */
5881
5882 /* global setTimeout: true */
5883
5884 /* short-circuit processing */
5885 if (curr[name].length === 0) return;
5886 /* iterate over all handlers, exactly once */
5887
5888 var handlers = curr[name];
5889 curr[name] = [];
5890 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
5891
5892 var func = function func() {
5893 for (var i = 0; i < handlers.length; i++) {
5894 handlers[i](value);
5895 }
5896 /* [Promises/A+ 2.2.5] */
5897
5898 };
5899 /* execute procedure asynchronously */
5900
5901 /* [Promises/A+ 2.2.4, 3.1] */
5902
5903
5904 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
5905};
5906/* generate a resolver function */
5907
5908
5909var resolver = function resolver(cb, next, method) {
5910 return function (value) {
5911 if (typeof cb !== 'function')
5912 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
5913 next[method].call(next, value);
5914 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
5915 else {
5916 var result;
5917
5918 try {
5919 result = cb(value);
5920 }
5921 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
5922 catch (e) {
5923 next.reject(e);
5924 /* [Promises/A+ 2.2.7.2] */
5925
5926 return;
5927 }
5928
5929 resolve(next, result);
5930 /* [Promises/A+ 2.2.7.1] */
5931 }
5932 };
5933};
5934/* "Promise Resolution Procedure" */
5935
5936/* [Promises/A+ 2.3] */
5937
5938
5939var resolve = function resolve(promise, x) {
5940 /* sanity check arguments */
5941
5942 /* [Promises/A+ 2.3.1] */
5943 if (promise === x || promise.proxy === x) {
5944 promise.reject(new TypeError('cannot resolve promise with itself'));
5945 return;
5946 }
5947 /* surgically check for a "then" method
5948 (mainly to just call the "getter" of "then" only once) */
5949
5950
5951 var then;
5952
5953 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
5954 try {
5955 then = x.then;
5956 }
5957 /* [Promises/A+ 2.3.3.1, 3.5] */
5958 catch (e) {
5959 promise.reject(e);
5960 /* [Promises/A+ 2.3.3.2] */
5961
5962 return;
5963 }
5964 }
5965 /* handle own Thenables [Promises/A+ 2.3.2]
5966 and similar "thenables" [Promises/A+ 2.3.3] */
5967
5968
5969 if (typeof then === 'function') {
5970 var resolved = false;
5971
5972 try {
5973 /* call retrieved "then" method */
5974
5975 /* [Promises/A+ 2.3.3.3] */
5976 then.call(x,
5977 /* resolvePromise */
5978
5979 /* [Promises/A+ 2.3.3.3.1] */
5980 function (y) {
5981 if (resolved) return;
5982 resolved = true;
5983 /* [Promises/A+ 2.3.3.3.3] */
5984
5985 if (y === x)
5986 /* [Promises/A+ 3.6] */
5987 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
5988 },
5989 /* rejectPromise */
5990
5991 /* [Promises/A+ 2.3.3.3.2] */
5992 function (r) {
5993 if (resolved) return;
5994 resolved = true;
5995 /* [Promises/A+ 2.3.3.3.3] */
5996
5997 promise.reject(r);
5998 });
5999 } catch (e) {
6000 if (!resolved)
6001 /* [Promises/A+ 2.3.3.3.3] */
6002 promise.reject(e);
6003 /* [Promises/A+ 2.3.3.3.4] */
6004 }
6005
6006 return;
6007 }
6008 /* handle other values */
6009
6010
6011 promise.fulfill(x);
6012 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6013}; // so we always have Promise.all()
6014
6015
6016api.all = function (ps) {
6017 return new api(function (resolveAll, rejectAll) {
6018 var vals = new Array(ps.length);
6019 var doneCount = 0;
6020
6021 var fulfill = function fulfill(i, val) {
6022 vals[i] = val;
6023 doneCount++;
6024
6025 if (doneCount === ps.length) {
6026 resolveAll(vals);
6027 }
6028 };
6029
6030 for (var i = 0; i < ps.length; i++) {
6031 (function (i) {
6032 var p = ps[i];
6033 var isPromise = p != null && p.then != null;
6034
6035 if (isPromise) {
6036 p.then(function (val) {
6037 fulfill(i, val);
6038 }, function (err) {
6039 rejectAll(err);
6040 });
6041 } else {
6042 var val = p;
6043 fulfill(i, val);
6044 }
6045 })(i);
6046 }
6047 });
6048};
6049
6050api.resolve = function (val) {
6051 return new api(function (resolve, reject) {
6052 resolve(val);
6053 });
6054};
6055
6056api.reject = function (val) {
6057 return new api(function (resolve, reject) {
6058 reject(val);
6059 });
6060};
6061
6062var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6063
6064var Animation = function Animation(target, opts, opts2) {
6065 var isCore = core(target);
6066 var isEle = !isCore;
6067
6068 var _p = this._private = extend({
6069 duration: 1000
6070 }, opts, opts2);
6071
6072 _p.target = target;
6073 _p.style = _p.style || _p.css;
6074 _p.started = false;
6075 _p.playing = false;
6076 _p.hooked = false;
6077 _p.applying = false;
6078 _p.progress = 0;
6079 _p.completes = [];
6080 _p.frames = [];
6081
6082 if (_p.complete && fn(_p.complete)) {
6083 _p.completes.push(_p.complete);
6084 }
6085
6086 if (isEle) {
6087 var pos = target.position();
6088 _p.startPosition = _p.startPosition || {
6089 x: pos.x,
6090 y: pos.y
6091 };
6092 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6093 }
6094
6095 if (isCore) {
6096 var pan = target.pan();
6097 _p.startPan = {
6098 x: pan.x,
6099 y: pan.y
6100 };
6101 _p.startZoom = target.zoom();
6102 } // for future timeline/animations impl
6103
6104
6105 this.length = 1;
6106 this[0] = this;
6107};
6108
6109var anifn = Animation.prototype;
6110extend(anifn, {
6111 instanceString: function instanceString() {
6112 return 'animation';
6113 },
6114 hook: function hook() {
6115 var _p = this._private;
6116
6117 if (!_p.hooked) {
6118 // add to target's animation queue
6119 var q;
6120 var tAni = _p.target._private.animation;
6121
6122 if (_p.queue) {
6123 q = tAni.queue;
6124 } else {
6125 q = tAni.current;
6126 }
6127
6128 q.push(this); // add to the animation loop pool
6129
6130 if (elementOrCollection(_p.target)) {
6131 _p.target.cy().addToAnimationPool(_p.target);
6132 }
6133
6134 _p.hooked = true;
6135 }
6136
6137 return this;
6138 },
6139 play: function play() {
6140 var _p = this._private; // autorewind
6141
6142 if (_p.progress === 1) {
6143 _p.progress = 0;
6144 }
6145
6146 _p.playing = true;
6147 _p.started = false; // needs to be started by animation loop
6148
6149 _p.stopped = false;
6150 this.hook(); // the animation loop will start the animation...
6151
6152 return this;
6153 },
6154 playing: function playing() {
6155 return this._private.playing;
6156 },
6157 apply: function apply() {
6158 var _p = this._private;
6159 _p.applying = true;
6160 _p.started = false; // needs to be started by animation loop
6161
6162 _p.stopped = false;
6163 this.hook(); // the animation loop will apply the animation at this progress
6164
6165 return this;
6166 },
6167 applying: function applying() {
6168 return this._private.applying;
6169 },
6170 pause: function pause() {
6171 var _p = this._private;
6172 _p.playing = false;
6173 _p.started = false;
6174 return this;
6175 },
6176 stop: function stop() {
6177 var _p = this._private;
6178 _p.playing = false;
6179 _p.started = false;
6180 _p.stopped = true; // to be removed from animation queues
6181
6182 return this;
6183 },
6184 rewind: function rewind() {
6185 return this.progress(0);
6186 },
6187 fastforward: function fastforward() {
6188 return this.progress(1);
6189 },
6190 time: function time(t) {
6191 var _p = this._private;
6192
6193 if (t === undefined) {
6194 return _p.progress * _p.duration;
6195 } else {
6196 return this.progress(t / _p.duration);
6197 }
6198 },
6199 progress: function progress(p) {
6200 var _p = this._private;
6201 var wasPlaying = _p.playing;
6202
6203 if (p === undefined) {
6204 return _p.progress;
6205 } else {
6206 if (wasPlaying) {
6207 this.pause();
6208 }
6209
6210 _p.progress = p;
6211 _p.started = false;
6212
6213 if (wasPlaying) {
6214 this.play();
6215 }
6216 }
6217
6218 return this;
6219 },
6220 completed: function completed() {
6221 return this._private.progress === 1;
6222 },
6223 reverse: function reverse() {
6224 var _p = this._private;
6225 var wasPlaying = _p.playing;
6226
6227 if (wasPlaying) {
6228 this.pause();
6229 }
6230
6231 _p.progress = 1 - _p.progress;
6232 _p.started = false;
6233
6234 var swap = function swap(a, b) {
6235 var _pa = _p[a];
6236
6237 if (_pa == null) {
6238 return;
6239 }
6240
6241 _p[a] = _p[b];
6242 _p[b] = _pa;
6243 };
6244
6245 swap('zoom', 'startZoom');
6246 swap('pan', 'startPan');
6247 swap('position', 'startPosition'); // swap styles
6248
6249 if (_p.style) {
6250 for (var i = 0; i < _p.style.length; i++) {
6251 var prop = _p.style[i];
6252 var name = prop.name;
6253 var startStyleProp = _p.startStyle[name];
6254 _p.startStyle[name] = prop;
6255 _p.style[i] = startStyleProp;
6256 }
6257 }
6258
6259 if (wasPlaying) {
6260 this.play();
6261 }
6262
6263 return this;
6264 },
6265 promise: function promise(type) {
6266 var _p = this._private;
6267 var arr;
6268
6269 switch (type) {
6270 case 'frame':
6271 arr = _p.frames;
6272 break;
6273
6274 default:
6275 case 'complete':
6276 case 'completed':
6277 arr = _p.completes;
6278 }
6279
6280 return new Promise$1(function (resolve, reject) {
6281 arr.push(function () {
6282 resolve();
6283 });
6284 });
6285 }
6286});
6287anifn.complete = anifn.completed;
6288anifn.run = anifn.play;
6289anifn.running = anifn.playing;
6290
6291var define = {
6292 animated: function animated() {
6293 return function animatedImpl() {
6294 var self = this;
6295 var selfIsArrayLike = self.length !== undefined;
6296 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6297
6298 var cy = this._private.cy || this;
6299
6300 if (!cy.styleEnabled()) {
6301 return false;
6302 }
6303
6304 var ele = all[0];
6305
6306 if (ele) {
6307 return ele._private.animation.current.length > 0;
6308 }
6309 };
6310 },
6311 // animated
6312 clearQueue: function clearQueue() {
6313 return function clearQueueImpl() {
6314 var self = this;
6315 var selfIsArrayLike = self.length !== undefined;
6316 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6317
6318 var cy = this._private.cy || this;
6319
6320 if (!cy.styleEnabled()) {
6321 return this;
6322 }
6323
6324 for (var i = 0; i < all.length; i++) {
6325 var ele = all[i];
6326 ele._private.animation.queue = [];
6327 }
6328
6329 return this;
6330 };
6331 },
6332 // clearQueue
6333 delay: function delay() {
6334 return function delayImpl(time, complete) {
6335 var cy = this._private.cy || this;
6336
6337 if (!cy.styleEnabled()) {
6338 return this;
6339 }
6340
6341 return this.animate({
6342 delay: time,
6343 duration: time,
6344 complete: complete
6345 });
6346 };
6347 },
6348 // delay
6349 delayAnimation: function delayAnimation() {
6350 return function delayAnimationImpl(time, complete) {
6351 var cy = this._private.cy || this;
6352
6353 if (!cy.styleEnabled()) {
6354 return this;
6355 }
6356
6357 return this.animation({
6358 delay: time,
6359 duration: time,
6360 complete: complete
6361 });
6362 };
6363 },
6364 // delay
6365 animation: function animation() {
6366 return function animationImpl(properties, params) {
6367 var self = this;
6368 var selfIsArrayLike = self.length !== undefined;
6369 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6370
6371 var cy = this._private.cy || this;
6372 var isCore = !selfIsArrayLike;
6373 var isEles = !isCore;
6374
6375 if (!cy.styleEnabled()) {
6376 return this;
6377 }
6378
6379 var style = cy.style();
6380 properties = extend({}, properties, params);
6381 var propertiesEmpty = Object.keys(properties).length === 0;
6382
6383 if (propertiesEmpty) {
6384 return new Animation(all[0], properties); // nothing to animate
6385 }
6386
6387 if (properties.duration === undefined) {
6388 properties.duration = 400;
6389 }
6390
6391 switch (properties.duration) {
6392 case 'slow':
6393 properties.duration = 600;
6394 break;
6395
6396 case 'fast':
6397 properties.duration = 200;
6398 break;
6399 }
6400
6401 if (isEles) {
6402 properties.style = style.getPropsList(properties.style || properties.css);
6403 properties.css = undefined;
6404 }
6405
6406 if (isEles && properties.renderedPosition != null) {
6407 var rpos = properties.renderedPosition;
6408 var pan = cy.pan();
6409 var zoom = cy.zoom();
6410 properties.position = renderedToModelPosition(rpos, zoom, pan);
6411 } // override pan w/ panBy if set
6412
6413
6414 if (isCore && properties.panBy != null) {
6415 var panBy = properties.panBy;
6416 var cyPan = cy.pan();
6417 properties.pan = {
6418 x: cyPan.x + panBy.x,
6419 y: cyPan.y + panBy.y
6420 };
6421 } // override pan w/ center if set
6422
6423
6424 var center = properties.center || properties.centre;
6425
6426 if (isCore && center != null) {
6427 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6428
6429 if (centerPan != null) {
6430 properties.pan = centerPan;
6431 }
6432 } // override pan & zoom w/ fit if set
6433
6434
6435 if (isCore && properties.fit != null) {
6436 var fit = properties.fit;
6437 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6438
6439 if (fitVp != null) {
6440 properties.pan = fitVp.pan;
6441 properties.zoom = fitVp.zoom;
6442 }
6443 } // override zoom (& potentially pan) w/ zoom obj if set
6444
6445
6446 if (isCore && plainObject(properties.zoom)) {
6447 var vp = cy.getZoomedViewport(properties.zoom);
6448
6449 if (vp != null) {
6450 if (vp.zoomed) {
6451 properties.zoom = vp.zoom;
6452 }
6453
6454 if (vp.panned) {
6455 properties.pan = vp.pan;
6456 }
6457 } else {
6458 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6459 }
6460 }
6461
6462 return new Animation(all[0], properties);
6463 };
6464 },
6465 // animate
6466 animate: function animate() {
6467 return function animateImpl(properties, params) {
6468 var self = this;
6469 var selfIsArrayLike = self.length !== undefined;
6470 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6471
6472 var cy = this._private.cy || this;
6473
6474 if (!cy.styleEnabled()) {
6475 return this;
6476 }
6477
6478 if (params) {
6479 properties = extend({}, properties, params);
6480 } // manually hook and run the animation
6481
6482
6483 for (var i = 0; i < all.length; i++) {
6484 var ele = all[i];
6485 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6486 var ani = ele.animation(properties, queue ? {
6487 queue: true
6488 } : undefined);
6489 ani.play();
6490 }
6491
6492 return this; // chaining
6493 };
6494 },
6495 // animate
6496 stop: function stop() {
6497 return function stopImpl(clearQueue, jumpToEnd) {
6498 var self = this;
6499 var selfIsArrayLike = self.length !== undefined;
6500 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6501
6502 var cy = this._private.cy || this;
6503
6504 if (!cy.styleEnabled()) {
6505 return this;
6506 }
6507
6508 for (var i = 0; i < all.length; i++) {
6509 var ele = all[i];
6510 var _p = ele._private;
6511 var anis = _p.animation.current;
6512
6513 for (var j = 0; j < anis.length; j++) {
6514 var ani = anis[j];
6515 var ani_p = ani._private;
6516
6517 if (jumpToEnd) {
6518 // next iteration of the animation loop, the animation
6519 // will go straight to the end and be removed
6520 ani_p.duration = 0;
6521 }
6522 } // clear the queue of future animations
6523
6524
6525 if (clearQueue) {
6526 _p.animation.queue = [];
6527 }
6528
6529 if (!jumpToEnd) {
6530 _p.animation.current = [];
6531 }
6532 } // we have to notify (the animation loop doesn't do it for us on `stop`)
6533
6534
6535 cy.notify('draw');
6536 return this;
6537 };
6538 } // stop
6539
6540}; // define
6541
6542var define$1 = {
6543 // access data field
6544 data: function data(params) {
6545 var defaults = {
6546 field: 'data',
6547 bindingEvent: 'data',
6548 allowBinding: false,
6549 allowSetting: false,
6550 allowGetting: false,
6551 settingEvent: 'data',
6552 settingTriggersEvent: false,
6553 triggerFnName: 'trigger',
6554 immutableKeys: {},
6555 // key => true if immutable
6556 updateStyle: false,
6557 beforeGet: function beforeGet(self) {},
6558 beforeSet: function beforeSet(self, obj) {},
6559 onSet: function onSet(self) {},
6560 canSet: function canSet(self) {
6561 return true;
6562 }
6563 };
6564 params = extend({}, defaults, params);
6565 return function dataImpl(name, value) {
6566 var p = params;
6567 var self = this;
6568 var selfIsArrayLike = self.length !== undefined;
6569 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6570
6571 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
6572
6573 if (string(name)) {
6574 // set or get property
6575 // .data('foo')
6576 if (p.allowGetting && value === undefined) {
6577 // get
6578 var ret;
6579
6580 if (single) {
6581 p.beforeGet(single);
6582 ret = single._private[p.field][name];
6583 }
6584
6585 return ret; // .data('foo', 'bar')
6586 } else if (p.allowSetting && value !== undefined) {
6587 // set
6588 var valid = !p.immutableKeys[name];
6589
6590 if (valid) {
6591 var change = _defineProperty({}, name, value);
6592
6593 p.beforeSet(self, change);
6594
6595 for (var i = 0, l = all.length; i < l; i++) {
6596 var ele = all[i];
6597
6598 if (p.canSet(ele)) {
6599 ele._private[p.field][name] = value;
6600 }
6601 } // update mappers if asked
6602
6603
6604 if (p.updateStyle) {
6605 self.updateStyle();
6606 } // call onSet callback
6607
6608
6609 p.onSet(self);
6610
6611 if (p.settingTriggersEvent) {
6612 self[p.triggerFnName](p.settingEvent);
6613 }
6614 }
6615 } // .data({ 'foo': 'bar' })
6616
6617 } else if (p.allowSetting && plainObject(name)) {
6618 // extend
6619 var obj = name;
6620 var k, v;
6621 var keys = Object.keys(obj);
6622 p.beforeSet(self, obj);
6623
6624 for (var _i = 0; _i < keys.length; _i++) {
6625 k = keys[_i];
6626 v = obj[k];
6627
6628 var _valid = !p.immutableKeys[k];
6629
6630 if (_valid) {
6631 for (var j = 0; j < all.length; j++) {
6632 var _ele = all[j];
6633
6634 if (p.canSet(_ele)) {
6635 _ele._private[p.field][k] = v;
6636 }
6637 }
6638 }
6639 } // update mappers if asked
6640
6641
6642 if (p.updateStyle) {
6643 self.updateStyle();
6644 } // call onSet callback
6645
6646
6647 p.onSet(self);
6648
6649 if (p.settingTriggersEvent) {
6650 self[p.triggerFnName](p.settingEvent);
6651 } // .data(function(){ ... })
6652
6653 } else if (p.allowBinding && fn(name)) {
6654 // bind to event
6655 var fn$1 = name;
6656 self.on(p.bindingEvent, fn$1); // .data()
6657 } else if (p.allowGetting && name === undefined) {
6658 // get whole object
6659 var _ret;
6660
6661 if (single) {
6662 p.beforeGet(single);
6663 _ret = single._private[p.field];
6664 }
6665
6666 return _ret;
6667 }
6668
6669 return self; // maintain chainability
6670 }; // function
6671 },
6672 // data
6673 // remove data field
6674 removeData: function removeData(params) {
6675 var defaults = {
6676 field: 'data',
6677 event: 'data',
6678 triggerFnName: 'trigger',
6679 triggerEvent: false,
6680 immutableKeys: {} // key => true if immutable
6681
6682 };
6683 params = extend({}, defaults, params);
6684 return function removeDataImpl(names) {
6685 var p = params;
6686 var self = this;
6687 var selfIsArrayLike = self.length !== undefined;
6688 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6689 // .removeData('foo bar')
6690
6691 if (string(names)) {
6692 // then get the list of keys, and delete them
6693 var keys = names.split(/\s+/);
6694 var l = keys.length;
6695
6696 for (var i = 0; i < l; i++) {
6697 // delete each non-empty key
6698 var key = keys[i];
6699
6700 if (emptyString(key)) {
6701 continue;
6702 }
6703
6704 var valid = !p.immutableKeys[key]; // not valid if immutable
6705
6706 if (valid) {
6707 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
6708 all[i_a]._private[p.field][key] = undefined;
6709 }
6710 }
6711 }
6712
6713 if (p.triggerEvent) {
6714 self[p.triggerFnName](p.event);
6715 } // .removeData()
6716
6717 } else if (names === undefined) {
6718 // then delete all keys
6719 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
6720 var _privateFields = all[_i_a]._private[p.field];
6721
6722 var _keys = Object.keys(_privateFields);
6723
6724 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
6725 var _key = _keys[_i2];
6726 var validKeyToDelete = !p.immutableKeys[_key];
6727
6728 if (validKeyToDelete) {
6729 _privateFields[_key] = undefined;
6730 }
6731 }
6732 }
6733
6734 if (p.triggerEvent) {
6735 self[p.triggerFnName](p.event);
6736 }
6737 }
6738
6739 return self; // maintain chaining
6740 }; // function
6741 } // removeData
6742
6743}; // define
6744
6745var define$2 = {
6746 eventAliasesOn: function eventAliasesOn(proto) {
6747 var p = proto;
6748 p.addListener = p.listen = p.bind = p.on;
6749 p.unlisten = p.unbind = p.off = p.removeListener;
6750 p.trigger = p.emit; // this is just a wrapper alias of .on()
6751
6752 p.pon = p.promiseOn = function (events, selector) {
6753 var self = this;
6754 var args = Array.prototype.slice.call(arguments, 0);
6755 return new Promise$1(function (resolve, reject) {
6756 var callback = function callback(e) {
6757 self.off.apply(self, offArgs);
6758 resolve(e);
6759 };
6760
6761 var onArgs = args.concat([callback]);
6762 var offArgs = onArgs.concat([]);
6763 self.on.apply(self, onArgs);
6764 });
6765 };
6766 }
6767}; // define
6768
6769// use this module to cherry pick functions into your prototype
6770var define$3 = {};
6771[define, define$1, define$2].forEach(function (m) {
6772 extend(define$3, m);
6773});
6774
6775var elesfn$d = {
6776 animate: define$3.animate(),
6777 animation: define$3.animation(),
6778 animated: define$3.animated(),
6779 clearQueue: define$3.clearQueue(),
6780 delay: define$3.delay(),
6781 delayAnimation: define$3.delayAnimation(),
6782 stop: define$3.stop()
6783};
6784
6785var elesfn$e = {
6786 classes: function classes(_classes) {
6787 var self = this;
6788
6789 if (_classes === undefined) {
6790 var ret = [];
6791
6792 self[0]._private.classes.forEach(function (cls) {
6793 return ret.push(cls);
6794 });
6795
6796 return ret;
6797 } else if (!array(_classes)) {
6798 // extract classes from string
6799 _classes = (_classes || '').match(/\S+/g) || [];
6800 }
6801
6802 var changed = [];
6803 var classesSet = new Set$1(_classes); // check and update each ele
6804
6805 for (var j = 0; j < self.length; j++) {
6806 var ele = self[j];
6807 var _p = ele._private;
6808 var eleClasses = _p.classes;
6809 var changedEle = false; // check if ele has all of the passed classes
6810
6811 for (var i = 0; i < _classes.length; i++) {
6812 var cls = _classes[i];
6813 var eleHasClass = eleClasses.has(cls);
6814
6815 if (!eleHasClass) {
6816 changedEle = true;
6817 break;
6818 }
6819 } // check if ele has classes outside of those passed
6820
6821
6822 if (!changedEle) {
6823 changedEle = eleClasses.size !== _classes.length;
6824 }
6825
6826 if (changedEle) {
6827 _p.classes = classesSet;
6828 changed.push(ele);
6829 }
6830 } // trigger update style on those eles that had class changes
6831
6832
6833 if (changed.length > 0) {
6834 this.spawn(changed).updateStyle().emit('class');
6835 }
6836
6837 return self;
6838 },
6839 addClass: function addClass(classes) {
6840 return this.toggleClass(classes, true);
6841 },
6842 hasClass: function hasClass(className) {
6843 var ele = this[0];
6844 return ele != null && ele._private.classes.has(className);
6845 },
6846 toggleClass: function toggleClass(classes, toggle) {
6847 if (!array(classes)) {
6848 // extract classes from string
6849 classes = classes.match(/\S+/g) || [];
6850 }
6851
6852 var self = this;
6853 var toggleUndefd = toggle === undefined;
6854 var changed = []; // eles who had classes changed
6855
6856 for (var i = 0, il = self.length; i < il; i++) {
6857 var ele = self[i];
6858 var eleClasses = ele._private.classes;
6859 var changedEle = false;
6860
6861 for (var j = 0; j < classes.length; j++) {
6862 var cls = classes[j];
6863 var hasClass = eleClasses.has(cls);
6864 var changedNow = false;
6865
6866 if (toggle || toggleUndefd && !hasClass) {
6867 eleClasses.add(cls);
6868 changedNow = true;
6869 } else if (!toggle || toggleUndefd && hasClass) {
6870 eleClasses["delete"](cls);
6871 changedNow = true;
6872 }
6873
6874 if (!changedEle && changedNow) {
6875 changed.push(ele);
6876 changedEle = true;
6877 }
6878 } // for j classes
6879
6880 } // for i eles
6881 // trigger update style on those eles that had class changes
6882
6883
6884 if (changed.length > 0) {
6885 this.spawn(changed).updateStyle().emit('class');
6886 }
6887
6888 return self;
6889 },
6890 removeClass: function removeClass(classes) {
6891 return this.toggleClass(classes, false);
6892 },
6893 flashClass: function flashClass(classes, duration) {
6894 var self = this;
6895
6896 if (duration == null) {
6897 duration = 250;
6898 } else if (duration === 0) {
6899 return self; // nothing to do really
6900 }
6901
6902 self.addClass(classes);
6903 setTimeout(function () {
6904 self.removeClass(classes);
6905 }, duration);
6906 return self;
6907 }
6908};
6909elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
6910
6911var tokens = {
6912 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
6913 // chars we need to escape in let names, etc
6914 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
6915 // binary comparison op (used in data selectors)
6916 boolOp: '\\?|\\!|\\^',
6917 // boolean (unary) operators (used in data selectors)
6918 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
6919 // string literals (used in data selectors) -- doublequotes | singlequotes
6920 number: number$1,
6921 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
6922 meta: 'degree|indegree|outdegree',
6923 // allowed metadata fields (i.e. allowed functions to use from Collection)
6924 separator: '\\s*,\\s*',
6925 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
6926 descendant: '\\s+',
6927 child: '\\s+>\\s+',
6928 subject: '\\$',
6929 group: 'node|edge|\\*',
6930 directedEdge: '\\s+->\\s+',
6931 undirectedEdge: '\\s+<->\\s+'
6932};
6933tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
6934
6935tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
6936
6937tokens.className = tokens.variable; // a class name (follows variable conventions)
6938
6939tokens.id = tokens.variable; // an element id (follows variable conventions)
6940
6941(function () {
6942 var ops, op, i; // add @ variants to comparatorOp
6943
6944 ops = tokens.comparatorOp.split('|');
6945
6946 for (i = 0; i < ops.length; i++) {
6947 op = ops[i];
6948 tokens.comparatorOp += '|@' + op;
6949 } // add ! variants to comparatorOp
6950
6951
6952 ops = tokens.comparatorOp.split('|');
6953
6954 for (i = 0; i < ops.length; i++) {
6955 op = ops[i];
6956
6957 if (op.indexOf('!') >= 0) {
6958 continue;
6959 } // skip ops that explicitly contain !
6960
6961
6962 if (op === '=') {
6963 continue;
6964 } // skip = b/c != is explicitly defined
6965
6966
6967 tokens.comparatorOp += '|\\!' + op;
6968 }
6969})();
6970
6971/**
6972 * Make a new query object
6973 *
6974 * @prop type {Type} The type enum (int) of the query
6975 * @prop checks List of checks to make against an ele to test for a match
6976 */
6977var newQuery = function newQuery() {
6978 return {
6979 checks: []
6980 };
6981};
6982
6983/**
6984 * A check type enum-like object. Uses integer values for fast match() lookup.
6985 * The ordering does not matter as long as the ints are unique.
6986 */
6987var Type = {
6988 /** E.g. node */
6989 GROUP: 0,
6990
6991 /** A collection of elements */
6992 COLLECTION: 1,
6993
6994 /** A filter(ele) function */
6995 FILTER: 2,
6996
6997 /** E.g. [foo > 1] */
6998 DATA_COMPARE: 3,
6999
7000 /** E.g. [foo] */
7001 DATA_EXIST: 4,
7002
7003 /** E.g. [?foo] */
7004 DATA_BOOL: 5,
7005
7006 /** E.g. [[degree > 2]] */
7007 META_COMPARE: 6,
7008
7009 /** E.g. :selected */
7010 STATE: 7,
7011
7012 /** E.g. #foo */
7013 ID: 8,
7014
7015 /** E.g. .foo */
7016 CLASS: 9,
7017
7018 /** E.g. #foo <-> #bar */
7019 UNDIRECTED_EDGE: 10,
7020
7021 /** E.g. #foo -> #bar */
7022 DIRECTED_EDGE: 11,
7023
7024 /** E.g. $#foo -> #bar */
7025 NODE_SOURCE: 12,
7026
7027 /** E.g. #foo -> $#bar */
7028 NODE_TARGET: 13,
7029
7030 /** E.g. $#foo <-> #bar */
7031 NODE_NEIGHBOR: 14,
7032
7033 /** E.g. #foo > #bar */
7034 CHILD: 15,
7035
7036 /** E.g. #foo #bar */
7037 DESCENDANT: 16,
7038
7039 /** E.g. $#foo > #bar */
7040 PARENT: 17,
7041
7042 /** E.g. $#foo #bar */
7043 ANCESTOR: 18,
7044
7045 /** E.g. #foo > $bar > #baz */
7046 COMPOUND_SPLIT: 19,
7047
7048 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7049 TRUE: 20
7050};
7051
7052var stateSelectors = [{
7053 selector: ':selected',
7054 matches: function matches(ele) {
7055 return ele.selected();
7056 }
7057}, {
7058 selector: ':unselected',
7059 matches: function matches(ele) {
7060 return !ele.selected();
7061 }
7062}, {
7063 selector: ':selectable',
7064 matches: function matches(ele) {
7065 return ele.selectable();
7066 }
7067}, {
7068 selector: ':unselectable',
7069 matches: function matches(ele) {
7070 return !ele.selectable();
7071 }
7072}, {
7073 selector: ':locked',
7074 matches: function matches(ele) {
7075 return ele.locked();
7076 }
7077}, {
7078 selector: ':unlocked',
7079 matches: function matches(ele) {
7080 return !ele.locked();
7081 }
7082}, {
7083 selector: ':visible',
7084 matches: function matches(ele) {
7085 return ele.visible();
7086 }
7087}, {
7088 selector: ':hidden',
7089 matches: function matches(ele) {
7090 return !ele.visible();
7091 }
7092}, {
7093 selector: ':transparent',
7094 matches: function matches(ele) {
7095 return ele.transparent();
7096 }
7097}, {
7098 selector: ':grabbed',
7099 matches: function matches(ele) {
7100 return ele.grabbed();
7101 }
7102}, {
7103 selector: ':free',
7104 matches: function matches(ele) {
7105 return !ele.grabbed();
7106 }
7107}, {
7108 selector: ':removed',
7109 matches: function matches(ele) {
7110 return ele.removed();
7111 }
7112}, {
7113 selector: ':inside',
7114 matches: function matches(ele) {
7115 return !ele.removed();
7116 }
7117}, {
7118 selector: ':grabbable',
7119 matches: function matches(ele) {
7120 return ele.grabbable();
7121 }
7122}, {
7123 selector: ':ungrabbable',
7124 matches: function matches(ele) {
7125 return !ele.grabbable();
7126 }
7127}, {
7128 selector: ':animated',
7129 matches: function matches(ele) {
7130 return ele.animated();
7131 }
7132}, {
7133 selector: ':unanimated',
7134 matches: function matches(ele) {
7135 return !ele.animated();
7136 }
7137}, {
7138 selector: ':parent',
7139 matches: function matches(ele) {
7140 return ele.isParent();
7141 }
7142}, {
7143 selector: ':childless',
7144 matches: function matches(ele) {
7145 return ele.isChildless();
7146 }
7147}, {
7148 selector: ':child',
7149 matches: function matches(ele) {
7150 return ele.isChild();
7151 }
7152}, {
7153 selector: ':orphan',
7154 matches: function matches(ele) {
7155 return ele.isOrphan();
7156 }
7157}, {
7158 selector: ':nonorphan',
7159 matches: function matches(ele) {
7160 return ele.isChild();
7161 }
7162}, {
7163 selector: ':compound',
7164 matches: function matches(ele) {
7165 if (ele.isNode()) {
7166 return ele.isParent();
7167 } else {
7168 return ele.source().isParent() || ele.target().isParent();
7169 }
7170 }
7171}, {
7172 selector: ':loop',
7173 matches: function matches(ele) {
7174 return ele.isLoop();
7175 }
7176}, {
7177 selector: ':simple',
7178 matches: function matches(ele) {
7179 return ele.isSimple();
7180 }
7181}, {
7182 selector: ':active',
7183 matches: function matches(ele) {
7184 return ele.active();
7185 }
7186}, {
7187 selector: ':inactive',
7188 matches: function matches(ele) {
7189 return !ele.active();
7190 }
7191}, {
7192 selector: ':backgrounding',
7193 matches: function matches(ele) {
7194 return ele.backgrounding();
7195 }
7196}, {
7197 selector: ':nonbackgrounding',
7198 matches: function matches(ele) {
7199 return !ele.backgrounding();
7200 }
7201}].sort(function (a, b) {
7202 // n.b. selectors that are starting substrings of others must have the longer ones first
7203 return descending(a.selector, b.selector);
7204});
7205
7206var lookup = function () {
7207 var selToFn = {};
7208 var s;
7209
7210 for (var i = 0; i < stateSelectors.length; i++) {
7211 s = stateSelectors[i];
7212 selToFn[s.selector] = s.matches;
7213 }
7214
7215 return selToFn;
7216}();
7217
7218var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7219 return lookup[sel](ele);
7220};
7221var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7222 return s.selector;
7223}).join('|') + ')';
7224
7225// so that values get compared properly in Selector.filter()
7226
7227var cleanMetaChars = function cleanMetaChars(str) {
7228 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7229 return $1;
7230 });
7231};
7232
7233var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7234 selector[selector.length - 1] = replacementQuery;
7235}; // NOTE: add new expression syntax here to have it recognised by the parser;
7236// - a query contains all adjacent (i.e. no separator in between) expressions;
7237// - the current query is stored in selector[i]
7238// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7239
7240
7241var exprs = [{
7242 name: 'group',
7243 // just used for identifying when debugging
7244 query: true,
7245 regex: '(' + tokens.group + ')',
7246 populate: function populate(selector, query, _ref) {
7247 var _ref2 = _slicedToArray(_ref, 1),
7248 group = _ref2[0];
7249
7250 query.checks.push({
7251 type: Type.GROUP,
7252 value: group === '*' ? group : group + 's'
7253 });
7254 }
7255}, {
7256 name: 'state',
7257 query: true,
7258 regex: stateSelectorRegex,
7259 populate: function populate(selector, query, _ref3) {
7260 var _ref4 = _slicedToArray(_ref3, 1),
7261 state = _ref4[0];
7262
7263 query.checks.push({
7264 type: Type.STATE,
7265 value: state
7266 });
7267 }
7268}, {
7269 name: 'id',
7270 query: true,
7271 regex: '\\#(' + tokens.id + ')',
7272 populate: function populate(selector, query, _ref5) {
7273 var _ref6 = _slicedToArray(_ref5, 1),
7274 id = _ref6[0];
7275
7276 query.checks.push({
7277 type: Type.ID,
7278 value: cleanMetaChars(id)
7279 });
7280 }
7281}, {
7282 name: 'className',
7283 query: true,
7284 regex: '\\.(' + tokens.className + ')',
7285 populate: function populate(selector, query, _ref7) {
7286 var _ref8 = _slicedToArray(_ref7, 1),
7287 className = _ref8[0];
7288
7289 query.checks.push({
7290 type: Type.CLASS,
7291 value: cleanMetaChars(className)
7292 });
7293 }
7294}, {
7295 name: 'dataExists',
7296 query: true,
7297 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
7298 populate: function populate(selector, query, _ref9) {
7299 var _ref10 = _slicedToArray(_ref9, 1),
7300 variable = _ref10[0];
7301
7302 query.checks.push({
7303 type: Type.DATA_EXIST,
7304 field: cleanMetaChars(variable)
7305 });
7306 }
7307}, {
7308 name: 'dataCompare',
7309 query: true,
7310 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
7311 populate: function populate(selector, query, _ref11) {
7312 var _ref12 = _slicedToArray(_ref11, 3),
7313 variable = _ref12[0],
7314 comparatorOp = _ref12[1],
7315 value = _ref12[2];
7316
7317 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
7318
7319 if (valueIsString) {
7320 value = value.substring(1, value.length - 1);
7321 } else {
7322 value = parseFloat(value);
7323 }
7324
7325 query.checks.push({
7326 type: Type.DATA_COMPARE,
7327 field: cleanMetaChars(variable),
7328 operator: comparatorOp,
7329 value: value
7330 });
7331 }
7332}, {
7333 name: 'dataBool',
7334 query: true,
7335 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
7336 populate: function populate(selector, query, _ref13) {
7337 var _ref14 = _slicedToArray(_ref13, 2),
7338 boolOp = _ref14[0],
7339 variable = _ref14[1];
7340
7341 query.checks.push({
7342 type: Type.DATA_BOOL,
7343 field: cleanMetaChars(variable),
7344 operator: boolOp
7345 });
7346 }
7347}, {
7348 name: 'metaCompare',
7349 query: true,
7350 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
7351 populate: function populate(selector, query, _ref15) {
7352 var _ref16 = _slicedToArray(_ref15, 3),
7353 meta = _ref16[0],
7354 comparatorOp = _ref16[1],
7355 number = _ref16[2];
7356
7357 query.checks.push({
7358 type: Type.META_COMPARE,
7359 field: cleanMetaChars(meta),
7360 operator: comparatorOp,
7361 value: parseFloat(number)
7362 });
7363 }
7364}, {
7365 name: 'nextQuery',
7366 separator: true,
7367 regex: tokens.separator,
7368 populate: function populate(selector, query) {
7369 var currentSubject = selector.currentSubject;
7370 var edgeCount = selector.edgeCount;
7371 var compoundCount = selector.compoundCount;
7372 var lastQ = selector[selector.length - 1];
7373
7374 if (currentSubject != null) {
7375 lastQ.subject = currentSubject;
7376 selector.currentSubject = null;
7377 }
7378
7379 lastQ.edgeCount = edgeCount;
7380 lastQ.compoundCount = compoundCount;
7381 selector.edgeCount = 0;
7382 selector.compoundCount = 0; // go on to next query
7383
7384 var nextQuery = selector[selector.length++] = newQuery();
7385 return nextQuery; // this is the new query to be filled by the following exprs
7386 }
7387}, {
7388 name: 'directedEdge',
7389 separator: true,
7390 regex: tokens.directedEdge,
7391 populate: function populate(selector, query) {
7392 if (selector.currentSubject == null) {
7393 // undirected edge
7394 var edgeQuery = newQuery();
7395 var source = query;
7396 var target = newQuery();
7397 edgeQuery.checks.push({
7398 type: Type.DIRECTED_EDGE,
7399 source: source,
7400 target: target
7401 }); // the query in the selector should be the edge rather than the source
7402
7403 replaceLastQuery(selector, query, edgeQuery);
7404 selector.edgeCount++; // we're now populating the target query with expressions that follow
7405
7406 return target;
7407 } else {
7408 // source/target
7409 var srcTgtQ = newQuery();
7410 var _source = query;
7411
7412 var _target = newQuery();
7413
7414 srcTgtQ.checks.push({
7415 type: Type.NODE_SOURCE,
7416 source: _source,
7417 target: _target
7418 }); // the query in the selector should be the neighbourhood rather than the node
7419
7420 replaceLastQuery(selector, query, srcTgtQ);
7421 selector.edgeCount++;
7422 return _target; // now populating the target with the following expressions
7423 }
7424 }
7425}, {
7426 name: 'undirectedEdge',
7427 separator: true,
7428 regex: tokens.undirectedEdge,
7429 populate: function populate(selector, query) {
7430 if (selector.currentSubject == null) {
7431 // undirected edge
7432 var edgeQuery = newQuery();
7433 var source = query;
7434 var target = newQuery();
7435 edgeQuery.checks.push({
7436 type: Type.UNDIRECTED_EDGE,
7437 nodes: [source, target]
7438 }); // the query in the selector should be the edge rather than the source
7439
7440 replaceLastQuery(selector, query, edgeQuery);
7441 selector.edgeCount++; // we're now populating the target query with expressions that follow
7442
7443 return target;
7444 } else {
7445 // neighbourhood
7446 var nhoodQ = newQuery();
7447 var node = query;
7448 var neighbor = newQuery();
7449 nhoodQ.checks.push({
7450 type: Type.NODE_NEIGHBOR,
7451 node: node,
7452 neighbor: neighbor
7453 }); // the query in the selector should be the neighbourhood rather than the node
7454
7455 replaceLastQuery(selector, query, nhoodQ);
7456 return neighbor; // now populating the neighbor with following expressions
7457 }
7458 }
7459}, {
7460 name: 'child',
7461 separator: true,
7462 regex: tokens.child,
7463 populate: function populate(selector, query) {
7464 if (selector.currentSubject == null) {
7465 // default: child query
7466 var parentChildQuery = newQuery();
7467 var child = newQuery();
7468 var parent = selector[selector.length - 1];
7469 parentChildQuery.checks.push({
7470 type: Type.CHILD,
7471 parent: parent,
7472 child: child
7473 }); // the query in the selector should be the '>' itself
7474
7475 replaceLastQuery(selector, query, parentChildQuery);
7476 selector.compoundCount++; // we're now populating the child query with expressions that follow
7477
7478 return child;
7479 } else if (selector.currentSubject === query) {
7480 // compound split query
7481 var compound = newQuery();
7482 var left = selector[selector.length - 1];
7483 var right = newQuery();
7484 var subject = newQuery();
7485
7486 var _child = newQuery();
7487
7488 var _parent = newQuery(); // set up the root compound q
7489
7490
7491 compound.checks.push({
7492 type: Type.COMPOUND_SPLIT,
7493 left: left,
7494 right: right,
7495 subject: subject
7496 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7497
7498 subject.checks = query.checks; // take the checks from the left
7499
7500 query.checks = [{
7501 type: Type.TRUE
7502 }]; // checks under left refs the subject implicitly
7503 // set up the right q
7504
7505 _parent.checks.push({
7506 type: Type.TRUE
7507 }); // parent implicitly refs the subject
7508
7509
7510 right.checks.push({
7511 type: Type.PARENT,
7512 // type is swapped on right side queries
7513 parent: _parent,
7514 child: _child // empty for now
7515
7516 });
7517 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7518
7519 selector.currentSubject = subject;
7520 selector.compoundCount++;
7521 return _child; // now populating the right side's child
7522 } else {
7523 // parent query
7524 // info for parent query
7525 var _parent2 = newQuery();
7526
7527 var _child2 = newQuery();
7528
7529 var pcQChecks = [{
7530 type: Type.PARENT,
7531 parent: _parent2,
7532 child: _child2
7533 }]; // the parent-child query takes the place of the query previously being populated
7534
7535 _parent2.checks = query.checks; // the previous query contains the checks for the parent
7536
7537 query.checks = pcQChecks; // pc query takes over
7538
7539 selector.compoundCount++;
7540 return _child2; // we're now populating the child
7541 }
7542 }
7543}, {
7544 name: 'descendant',
7545 separator: true,
7546 regex: tokens.descendant,
7547 populate: function populate(selector, query) {
7548 if (selector.currentSubject == null) {
7549 // default: descendant query
7550 var ancChQuery = newQuery();
7551 var descendant = newQuery();
7552 var ancestor = selector[selector.length - 1];
7553 ancChQuery.checks.push({
7554 type: Type.DESCENDANT,
7555 ancestor: ancestor,
7556 descendant: descendant
7557 }); // the query in the selector should be the '>' itself
7558
7559 replaceLastQuery(selector, query, ancChQuery);
7560 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
7561
7562 return descendant;
7563 } else if (selector.currentSubject === query) {
7564 // compound split query
7565 var compound = newQuery();
7566 var left = selector[selector.length - 1];
7567 var right = newQuery();
7568 var subject = newQuery();
7569
7570 var _descendant = newQuery();
7571
7572 var _ancestor = newQuery(); // set up the root compound q
7573
7574
7575 compound.checks.push({
7576 type: Type.COMPOUND_SPLIT,
7577 left: left,
7578 right: right,
7579 subject: subject
7580 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7581
7582 subject.checks = query.checks; // take the checks from the left
7583
7584 query.checks = [{
7585 type: Type.TRUE
7586 }]; // checks under left refs the subject implicitly
7587 // set up the right q
7588
7589 _ancestor.checks.push({
7590 type: Type.TRUE
7591 }); // ancestor implicitly refs the subject
7592
7593
7594 right.checks.push({
7595 type: Type.ANCESTOR,
7596 // type is swapped on right side queries
7597 ancestor: _ancestor,
7598 descendant: _descendant // empty for now
7599
7600 });
7601 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7602
7603 selector.currentSubject = subject;
7604 selector.compoundCount++;
7605 return _descendant; // now populating the right side's descendant
7606 } else {
7607 // ancestor query
7608 // info for parent query
7609 var _ancestor2 = newQuery();
7610
7611 var _descendant2 = newQuery();
7612
7613 var adQChecks = [{
7614 type: Type.ANCESTOR,
7615 ancestor: _ancestor2,
7616 descendant: _descendant2
7617 }]; // the parent-child query takes the place of the query previously being populated
7618
7619 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
7620
7621 query.checks = adQChecks; // pc query takes over
7622
7623 selector.compoundCount++;
7624 return _descendant2; // we're now populating the child
7625 }
7626 }
7627}, {
7628 name: 'subject',
7629 modifier: true,
7630 regex: tokens.subject,
7631 populate: function populate(selector, query) {
7632 if (selector.currentSubject != null && selector.currentSubject !== query) {
7633 warn('Redefinition of subject in selector `' + selector.toString() + '`');
7634 return false;
7635 }
7636
7637 selector.currentSubject = query;
7638 var topQ = selector[selector.length - 1];
7639 var topChk = topQ.checks[0];
7640 var topType = topChk == null ? null : topChk.type;
7641
7642 if (topType === Type.DIRECTED_EDGE) {
7643 // directed edge with subject on the target
7644 // change to target node check
7645 topChk.type = Type.NODE_TARGET;
7646 } else if (topType === Type.UNDIRECTED_EDGE) {
7647 // undirected edge with subject on the second node
7648 // change to neighbor check
7649 topChk.type = Type.NODE_NEIGHBOR;
7650 topChk.node = topChk.nodes[1]; // second node is subject
7651
7652 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
7653
7654 topChk.nodes = null;
7655 }
7656 }
7657}];
7658exprs.forEach(function (e) {
7659 return e.regexObj = new RegExp('^' + e.regex);
7660});
7661
7662/**
7663 * Of all the expressions, find the first match in the remaining text.
7664 * @param {string} remaining The remaining text to parse
7665 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
7666 */
7667
7668var consumeExpr = function consumeExpr(remaining) {
7669 var expr;
7670 var match;
7671 var name;
7672
7673 for (var j = 0; j < exprs.length; j++) {
7674 var e = exprs[j];
7675 var n = e.name;
7676 var m = remaining.match(e.regexObj);
7677
7678 if (m != null) {
7679 match = m;
7680 expr = e;
7681 name = n;
7682 var consumed = m[0];
7683 remaining = remaining.substring(consumed.length);
7684 break; // we've consumed one expr, so we can return now
7685 }
7686 }
7687
7688 return {
7689 expr: expr,
7690 match: match,
7691 name: name,
7692 remaining: remaining
7693 };
7694};
7695/**
7696 * Consume all the leading whitespace
7697 * @param {string} remaining The text to consume
7698 * @returns The text with the leading whitespace removed
7699 */
7700
7701
7702var consumeWhitespace = function consumeWhitespace(remaining) {
7703 var match = remaining.match(/^\s+/);
7704
7705 if (match) {
7706 var consumed = match[0];
7707 remaining = remaining.substring(consumed.length);
7708 }
7709
7710 return remaining;
7711};
7712/**
7713 * Parse the string and store the parsed representation in the Selector.
7714 * @param {string} selector The selector string
7715 * @returns `true` if the selector was successfully parsed, `false` otherwise
7716 */
7717
7718
7719var parse = function parse(selector) {
7720 var self = this;
7721 var remaining = self.inputText = selector;
7722 var currentQuery = self[0] = newQuery();
7723 self.length = 1;
7724 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
7725
7726 for (;;) {
7727 var exprInfo = consumeExpr(remaining);
7728
7729 if (exprInfo.expr == null) {
7730 warn('The selector `' + selector + '`is invalid');
7731 return false;
7732 } else {
7733 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
7734
7735 var ret = exprInfo.expr.populate(self, currentQuery, args);
7736
7737 if (ret === false) {
7738 return false; // exit if population failed
7739 } else if (ret != null) {
7740 currentQuery = ret; // change the current query to be filled if the expr specifies
7741 }
7742 }
7743
7744 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
7745
7746 if (remaining.match(/^\s*$/)) {
7747 break;
7748 }
7749 }
7750
7751 var lastQ = self[self.length - 1];
7752
7753 if (self.currentSubject != null) {
7754 lastQ.subject = self.currentSubject;
7755 }
7756
7757 lastQ.edgeCount = self.edgeCount;
7758 lastQ.compoundCount = self.compoundCount;
7759
7760 for (var i = 0; i < self.length; i++) {
7761 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
7762
7763 if (q.compoundCount > 0 && q.edgeCount > 0) {
7764 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
7765 return false;
7766 }
7767
7768 if (q.edgeCount > 1) {
7769 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
7770 return false;
7771 } else if (q.edgeCount === 1) {
7772 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.');
7773 }
7774 }
7775
7776 return true; // success
7777};
7778/**
7779 * Get the selector represented as a string. This value uses default formatting,
7780 * so things like spacing may differ from the input text passed to the constructor.
7781 * @returns {string} The selector string
7782 */
7783
7784
7785var toString = function toString() {
7786 if (this.toStringCache != null) {
7787 return this.toStringCache;
7788 }
7789
7790 var clean = function clean(obj) {
7791 if (obj == null) {
7792 return '';
7793 } else {
7794 return obj;
7795 }
7796 };
7797
7798 var cleanVal = function cleanVal(val) {
7799 if (string(val)) {
7800 return '"' + val + '"';
7801 } else {
7802 return clean(val);
7803 }
7804 };
7805
7806 var space = function space(val) {
7807 return ' ' + val + ' ';
7808 };
7809
7810 var checkToString = function checkToString(check, subject) {
7811 var type = check.type,
7812 value = check.value;
7813
7814 switch (type) {
7815 case Type.GROUP:
7816 {
7817 var group = clean(value);
7818 return group.substring(0, group.length - 1);
7819 }
7820
7821 case Type.DATA_COMPARE:
7822 {
7823 var field = check.field,
7824 operator = check.operator;
7825 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
7826 }
7827
7828 case Type.DATA_BOOL:
7829 {
7830 var _operator = check.operator,
7831 _field = check.field;
7832 return '[' + clean(_operator) + _field + ']';
7833 }
7834
7835 case Type.DATA_EXIST:
7836 {
7837 var _field2 = check.field;
7838 return '[' + _field2 + ']';
7839 }
7840
7841 case Type.META_COMPARE:
7842 {
7843 var _operator2 = check.operator,
7844 _field3 = check.field;
7845 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
7846 }
7847
7848 case Type.STATE:
7849 {
7850 return value;
7851 }
7852
7853 case Type.ID:
7854 {
7855 return '#' + value;
7856 }
7857
7858 case Type.CLASS:
7859 {
7860 return '.' + value;
7861 }
7862
7863 case Type.PARENT:
7864 case Type.CHILD:
7865 {
7866 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
7867 }
7868
7869 case Type.ANCESTOR:
7870 case Type.DESCENDANT:
7871 {
7872 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
7873 }
7874
7875 case Type.COMPOUND_SPLIT:
7876 {
7877 var lhs = queryToString(check.left, subject);
7878 var sub = queryToString(check.subject, subject);
7879 var rhs = queryToString(check.right, subject);
7880 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
7881 }
7882
7883 case Type.TRUE:
7884 {
7885 return '';
7886 }
7887 }
7888 };
7889
7890 var queryToString = function queryToString(query, subject) {
7891 return query.checks.reduce(function (str, chk, i) {
7892 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
7893 }, '');
7894 };
7895
7896 var str = '';
7897
7898 for (var i = 0; i < this.length; i++) {
7899 var query = this[i];
7900 str += queryToString(query, query.subject);
7901
7902 if (this.length > 1 && i < this.length - 1) {
7903 str += ', ';
7904 }
7905 }
7906
7907 this.toStringCache = str;
7908 return str;
7909};
7910var parse$1 = {
7911 parse: parse,
7912 toString: toString
7913};
7914
7915var valCmp = function valCmp(fieldVal, operator, value) {
7916 var matches;
7917 var isFieldStr = string(fieldVal);
7918 var isFieldNum = number(fieldVal);
7919 var isValStr = string(value);
7920 var fieldStr, valStr;
7921 var caseInsensitive = false;
7922 var notExpr = false;
7923 var isIneqCmp = false;
7924
7925 if (operator.indexOf('!') >= 0) {
7926 operator = operator.replace('!', '');
7927 notExpr = true;
7928 }
7929
7930 if (operator.indexOf('@') >= 0) {
7931 operator = operator.replace('@', '');
7932 caseInsensitive = true;
7933 }
7934
7935 if (isFieldStr || isValStr || caseInsensitive) {
7936 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
7937 valStr = '' + value;
7938 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
7939 // even if we're comparing numbers
7940
7941
7942 if (caseInsensitive) {
7943 fieldVal = fieldStr = fieldStr.toLowerCase();
7944 value = valStr = valStr.toLowerCase();
7945 }
7946
7947 switch (operator) {
7948 case '*=':
7949 matches = fieldStr.indexOf(valStr) >= 0;
7950 break;
7951
7952 case '$=':
7953 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
7954 break;
7955
7956 case '^=':
7957 matches = fieldStr.indexOf(valStr) === 0;
7958 break;
7959
7960 case '=':
7961 matches = fieldVal === value;
7962 break;
7963
7964 case '>':
7965 isIneqCmp = true;
7966 matches = fieldVal > value;
7967 break;
7968
7969 case '>=':
7970 isIneqCmp = true;
7971 matches = fieldVal >= value;
7972 break;
7973
7974 case '<':
7975 isIneqCmp = true;
7976 matches = fieldVal < value;
7977 break;
7978
7979 case '<=':
7980 isIneqCmp = true;
7981 matches = fieldVal <= value;
7982 break;
7983
7984 default:
7985 matches = false;
7986 break;
7987 } // apply the not op, but null vals for inequalities should always stay non-matching
7988
7989
7990 if (notExpr && (fieldVal != null || !isIneqCmp)) {
7991 matches = !matches;
7992 }
7993
7994 return matches;
7995};
7996var boolCmp = function boolCmp(fieldVal, operator) {
7997 switch (operator) {
7998 case '?':
7999 return fieldVal ? true : false;
8000
8001 case '!':
8002 return fieldVal ? false : true;
8003
8004 case '^':
8005 return fieldVal === undefined;
8006 }
8007};
8008var existCmp = function existCmp(fieldVal) {
8009 return fieldVal !== undefined;
8010};
8011var data = function data(ele, field) {
8012 return ele.data(field);
8013};
8014var meta = function meta(ele, field) {
8015 return ele[field]();
8016};
8017
8018/** A lookup of `match(check, ele)` functions by `Type` int */
8019
8020var match = [];
8021/**
8022 * Returns whether the query matches for the element
8023 * @param query The `{ type, value, ... }` query object
8024 * @param ele The element to compare against
8025*/
8026
8027var matches = function matches(query, ele) {
8028 return query.checks.every(function (chk) {
8029 return match[chk.type](chk, ele);
8030 });
8031};
8032
8033match[Type.GROUP] = function (check, ele) {
8034 var group = check.value;
8035 return group === '*' || group === ele.group();
8036};
8037
8038match[Type.STATE] = function (check, ele) {
8039 var stateSelector = check.value;
8040 return stateSelectorMatches(stateSelector, ele);
8041};
8042
8043match[Type.ID] = function (check, ele) {
8044 var id = check.value;
8045 return ele.id() === id;
8046};
8047
8048match[Type.CLASS] = function (check, ele) {
8049 var cls = check.value;
8050 return ele.hasClass(cls);
8051};
8052
8053match[Type.META_COMPARE] = function (check, ele) {
8054 var field = check.field,
8055 operator = check.operator,
8056 value = check.value;
8057 return valCmp(meta(ele, field), operator, value);
8058};
8059
8060match[Type.DATA_COMPARE] = function (check, ele) {
8061 var field = check.field,
8062 operator = check.operator,
8063 value = check.value;
8064 return valCmp(data(ele, field), operator, value);
8065};
8066
8067match[Type.DATA_BOOL] = function (check, ele) {
8068 var field = check.field,
8069 operator = check.operator;
8070 return boolCmp(data(ele, field), operator);
8071};
8072
8073match[Type.DATA_EXIST] = function (check, ele) {
8074 var field = check.field,
8075 operator = check.operator;
8076 return existCmp(data(ele, field));
8077};
8078
8079match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8080 var qA = check.nodes[0];
8081 var qB = check.nodes[1];
8082 var src = ele.source();
8083 var tgt = ele.target();
8084 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
8085};
8086
8087match[Type.NODE_NEIGHBOR] = function (check, ele) {
8088 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
8089 return n.isNode() && matches(check.neighbor, n);
8090 });
8091};
8092
8093match[Type.DIRECTED_EDGE] = function (check, ele) {
8094 return matches(check.source, ele.source()) && matches(check.target, ele.target());
8095};
8096
8097match[Type.NODE_SOURCE] = function (check, ele) {
8098 return matches(check.source, ele) && ele.outgoers().some(function (n) {
8099 return n.isNode() && matches(check.target, n);
8100 });
8101};
8102
8103match[Type.NODE_TARGET] = function (check, ele) {
8104 return matches(check.target, ele) && ele.incomers().some(function (n) {
8105 return n.isNode() && matches(check.source, n);
8106 });
8107};
8108
8109match[Type.CHILD] = function (check, ele) {
8110 return matches(check.child, ele) && matches(check.parent, ele.parent());
8111};
8112
8113match[Type.PARENT] = function (check, ele) {
8114 return matches(check.parent, ele) && ele.children().some(function (c) {
8115 return matches(check.child, c);
8116 });
8117};
8118
8119match[Type.DESCENDANT] = function (check, ele) {
8120 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
8121 return matches(check.ancestor, a);
8122 });
8123};
8124
8125match[Type.ANCESTOR] = function (check, ele) {
8126 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
8127 return matches(check.descendant, d);
8128 });
8129};
8130
8131match[Type.COMPOUND_SPLIT] = function (check, ele) {
8132 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
8133};
8134
8135match[Type.TRUE] = function () {
8136 return true;
8137};
8138
8139match[Type.COLLECTION] = function (check, ele) {
8140 var collection = check.value;
8141 return collection.has(ele);
8142};
8143
8144match[Type.FILTER] = function (check, ele) {
8145 var filter = check.value;
8146 return filter(ele);
8147};
8148
8149var filter = function filter(collection) {
8150 var self = this; // for 1 id #foo queries, just get the element
8151
8152 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8153 return collection.getElementById(self[0].checks[0].value).collection();
8154 }
8155
8156 var selectorFunction = function selectorFunction(element) {
8157 for (var j = 0; j < self.length; j++) {
8158 var query = self[j];
8159
8160 if (matches(query, element)) {
8161 return true;
8162 }
8163 }
8164
8165 return false;
8166 };
8167
8168 if (self.text() == null) {
8169 selectorFunction = function selectorFunction() {
8170 return true;
8171 };
8172 }
8173
8174 return collection.filter(selectorFunction);
8175}; // filter
8176// does selector match a single element?
8177
8178
8179var matches$1 = function matches$1(ele) {
8180 var self = this;
8181
8182 for (var j = 0; j < self.length; j++) {
8183 var query = self[j];
8184
8185 if (matches(query, ele)) {
8186 return true;
8187 }
8188 }
8189
8190 return false;
8191}; // matches
8192
8193
8194var matching = {
8195 matches: matches$1,
8196 filter: filter
8197};
8198
8199var Selector = function Selector(selector) {
8200 this.inputText = selector;
8201 this.currentSubject = null;
8202 this.compoundCount = 0;
8203 this.edgeCount = 0;
8204 this.length = 0;
8205
8206 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8207 this.addQuery({
8208 checks: [{
8209 type: Type.COLLECTION,
8210 value: selector.collection()
8211 }]
8212 });
8213 } else if (fn(selector)) {
8214 this.addQuery({
8215 checks: [{
8216 type: Type.FILTER,
8217 value: selector
8218 }]
8219 });
8220 } else if (string(selector)) {
8221 if (!this.parse(selector)) {
8222 this.invalid = true;
8223 }
8224 } else {
8225 error('A selector must be created from a string; found ');
8226 }
8227};
8228
8229var selfn = Selector.prototype;
8230[parse$1, matching].forEach(function (p) {
8231 return extend(selfn, p);
8232});
8233
8234selfn.text = function () {
8235 return this.inputText;
8236};
8237
8238selfn.size = function () {
8239 return this.length;
8240};
8241
8242selfn.eq = function (i) {
8243 return this[i];
8244};
8245
8246selfn.sameText = function (otherSel) {
8247 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8248};
8249
8250selfn.addQuery = function (q) {
8251 this[this.length++] = q;
8252};
8253
8254selfn.selector = selfn.toString;
8255
8256var elesfn$f = {
8257 allAre: function allAre(selector) {
8258 var selObj = new Selector(selector);
8259 return this.every(function (ele) {
8260 return selObj.matches(ele);
8261 });
8262 },
8263 is: function is(selector) {
8264 var selObj = new Selector(selector);
8265 return this.some(function (ele) {
8266 return selObj.matches(ele);
8267 });
8268 },
8269 some: function some(fn, thisArg) {
8270 for (var i = 0; i < this.length; i++) {
8271 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8272
8273 if (ret) {
8274 return true;
8275 }
8276 }
8277
8278 return false;
8279 },
8280 every: function every(fn, thisArg) {
8281 for (var i = 0; i < this.length; i++) {
8282 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8283
8284 if (!ret) {
8285 return false;
8286 }
8287 }
8288
8289 return true;
8290 },
8291 same: function same(collection) {
8292 // cheap collection ref check
8293 if (this === collection) {
8294 return true;
8295 }
8296
8297 collection = this.cy().collection(collection);
8298 var thisLength = this.length;
8299 var collectionLength = collection.length; // cheap length check
8300
8301 if (thisLength !== collectionLength) {
8302 return false;
8303 } // cheap element ref check
8304
8305
8306 if (thisLength === 1) {
8307 return this[0] === collection[0];
8308 }
8309
8310 return this.every(function (ele) {
8311 return collection.hasElementWithId(ele.id());
8312 });
8313 },
8314 anySame: function anySame(collection) {
8315 collection = this.cy().collection(collection);
8316 return this.some(function (ele) {
8317 return collection.hasElementWithId(ele.id());
8318 });
8319 },
8320 allAreNeighbors: function allAreNeighbors(collection) {
8321 collection = this.cy().collection(collection);
8322 var nhood = this.neighborhood();
8323 return collection.every(function (ele) {
8324 return nhood.hasElementWithId(ele.id());
8325 });
8326 },
8327 contains: function contains(collection) {
8328 collection = this.cy().collection(collection);
8329 var self = this;
8330 return collection.every(function (ele) {
8331 return self.hasElementWithId(ele.id());
8332 });
8333 }
8334};
8335elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
8336elesfn$f.has = elesfn$f.contains;
8337elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
8338
8339var cache = function cache(fn, name) {
8340 return function traversalCache(arg1, arg2, arg3, arg4) {
8341 var selectorOrEles = arg1;
8342 var eles = this;
8343 var key;
8344
8345 if (selectorOrEles == null) {
8346 key = '';
8347 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
8348 key = selectorOrEles.id();
8349 }
8350
8351 if (eles.length === 1 && key) {
8352 var _p = eles[0]._private;
8353 var tch = _p.traversalCache = _p.traversalCache || {};
8354 var ch = tch[name] = tch[name] || [];
8355 var hash = hashString(key);
8356 var cacheHit = ch[hash];
8357
8358 if (cacheHit) {
8359 return cacheHit;
8360 } else {
8361 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
8362 }
8363 } else {
8364 return fn.call(eles, arg1, arg2, arg3, arg4);
8365 }
8366 };
8367};
8368
8369var elesfn$g = {
8370 parent: function parent(selector) {
8371 var parents = []; // optimisation for single ele call
8372
8373 if (this.length === 1) {
8374 var parent = this[0]._private.parent;
8375
8376 if (parent) {
8377 return parent;
8378 }
8379 }
8380
8381 for (var i = 0; i < this.length; i++) {
8382 var ele = this[i];
8383 var _parent = ele._private.parent;
8384
8385 if (_parent) {
8386 parents.push(_parent);
8387 }
8388 }
8389
8390 return this.spawn(parents, true).filter(selector);
8391 },
8392 parents: function parents(selector) {
8393 var parents = [];
8394 var eles = this.parent();
8395
8396 while (eles.nonempty()) {
8397 for (var i = 0; i < eles.length; i++) {
8398 var ele = eles[i];
8399 parents.push(ele);
8400 }
8401
8402 eles = eles.parent();
8403 }
8404
8405 return this.spawn(parents, true).filter(selector);
8406 },
8407 commonAncestors: function commonAncestors(selector) {
8408 var ancestors;
8409
8410 for (var i = 0; i < this.length; i++) {
8411 var ele = this[i];
8412 var parents = ele.parents();
8413 ancestors = ancestors || parents;
8414 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
8415 }
8416
8417 return ancestors.filter(selector);
8418 },
8419 orphans: function orphans(selector) {
8420 return this.stdFilter(function (ele) {
8421 return ele.isOrphan();
8422 }).filter(selector);
8423 },
8424 nonorphans: function nonorphans(selector) {
8425 return this.stdFilter(function (ele) {
8426 return ele.isChild();
8427 }).filter(selector);
8428 },
8429 children: cache(function (selector) {
8430 var children = [];
8431
8432 for (var i = 0; i < this.length; i++) {
8433 var ele = this[i];
8434 var eleChildren = ele._private.children;
8435
8436 for (var j = 0; j < eleChildren.length; j++) {
8437 children.push(eleChildren[j]);
8438 }
8439 }
8440
8441 return this.spawn(children, true).filter(selector);
8442 }, 'children'),
8443 siblings: function siblings(selector) {
8444 return this.parent().children().not(this).filter(selector);
8445 },
8446 isParent: function isParent() {
8447 var ele = this[0];
8448
8449 if (ele) {
8450 return ele.isNode() && ele._private.children.length !== 0;
8451 }
8452 },
8453 isChildless: function isChildless() {
8454 var ele = this[0];
8455
8456 if (ele) {
8457 return ele.isNode() && ele._private.children.length === 0;
8458 }
8459 },
8460 isChild: function isChild() {
8461 var ele = this[0];
8462
8463 if (ele) {
8464 return ele.isNode() && ele._private.parent != null;
8465 }
8466 },
8467 isOrphan: function isOrphan() {
8468 var ele = this[0];
8469
8470 if (ele) {
8471 return ele.isNode() && ele._private.parent == null;
8472 }
8473 },
8474 descendants: function descendants(selector) {
8475 var elements = [];
8476
8477 function add(eles) {
8478 for (var i = 0; i < eles.length; i++) {
8479 var ele = eles[i];
8480 elements.push(ele);
8481
8482 if (ele.children().nonempty()) {
8483 add(ele.children());
8484 }
8485 }
8486 }
8487
8488 add(this.children());
8489 return this.spawn(elements, true).filter(selector);
8490 }
8491};
8492
8493function forEachCompound(eles, fn, includeSelf, recursiveStep) {
8494 var q = [];
8495 var did = new Set$1();
8496 var cy = eles.cy();
8497 var hasCompounds = cy.hasCompoundNodes();
8498
8499 for (var i = 0; i < eles.length; i++) {
8500 var ele = eles[i];
8501
8502 if (includeSelf) {
8503 q.push(ele);
8504 } else if (hasCompounds) {
8505 recursiveStep(q, did, ele);
8506 }
8507 }
8508
8509 while (q.length > 0) {
8510 var _ele = q.shift();
8511
8512 fn(_ele);
8513 did.add(_ele.id());
8514
8515 if (hasCompounds) {
8516 recursiveStep(q, did, _ele);
8517 }
8518 }
8519
8520 return eles;
8521}
8522
8523function addChildren(q, did, ele) {
8524 if (ele.isParent()) {
8525 var children = ele._private.children;
8526
8527 for (var i = 0; i < children.length; i++) {
8528 var child = children[i];
8529
8530 if (!did.has(child.id())) {
8531 q.push(child);
8532 }
8533 }
8534 }
8535} // very efficient version of eles.add( eles.descendants() ).forEach()
8536// for internal use
8537
8538
8539elesfn$g.forEachDown = function (fn) {
8540 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8541 return forEachCompound(this, fn, includeSelf, addChildren);
8542};
8543
8544function addParent(q, did, ele) {
8545 if (ele.isChild()) {
8546 var parent = ele._private.parent;
8547
8548 if (!did.has(parent.id())) {
8549 q.push(parent);
8550 }
8551 }
8552}
8553
8554elesfn$g.forEachUp = function (fn) {
8555 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8556 return forEachCompound(this, fn, includeSelf, addParent);
8557};
8558
8559function addParentAndChildren(q, did, ele) {
8560 addParent(q, did, ele);
8561 addChildren(q, did, ele);
8562}
8563
8564elesfn$g.forEachUpAndDown = function (fn) {
8565 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8566 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
8567}; // aliases
8568
8569
8570elesfn$g.ancestors = elesfn$g.parents;
8571
8572var fn$1, elesfn$h;
8573fn$1 = elesfn$h = {
8574 data: define$3.data({
8575 field: 'data',
8576 bindingEvent: 'data',
8577 allowBinding: true,
8578 allowSetting: true,
8579 settingEvent: 'data',
8580 settingTriggersEvent: true,
8581 triggerFnName: 'trigger',
8582 allowGetting: true,
8583 immutableKeys: {
8584 'id': true,
8585 'source': true,
8586 'target': true,
8587 'parent': true
8588 },
8589 updateStyle: true
8590 }),
8591 removeData: define$3.removeData({
8592 field: 'data',
8593 event: 'data',
8594 triggerFnName: 'trigger',
8595 triggerEvent: true,
8596 immutableKeys: {
8597 'id': true,
8598 'source': true,
8599 'target': true,
8600 'parent': true
8601 },
8602 updateStyle: true
8603 }),
8604 scratch: define$3.data({
8605 field: 'scratch',
8606 bindingEvent: 'scratch',
8607 allowBinding: true,
8608 allowSetting: true,
8609 settingEvent: 'scratch',
8610 settingTriggersEvent: true,
8611 triggerFnName: 'trigger',
8612 allowGetting: true,
8613 updateStyle: true
8614 }),
8615 removeScratch: define$3.removeData({
8616 field: 'scratch',
8617 event: 'scratch',
8618 triggerFnName: 'trigger',
8619 triggerEvent: true,
8620 updateStyle: true
8621 }),
8622 rscratch: define$3.data({
8623 field: 'rscratch',
8624 allowBinding: false,
8625 allowSetting: true,
8626 settingTriggersEvent: false,
8627 allowGetting: true
8628 }),
8629 removeRscratch: define$3.removeData({
8630 field: 'rscratch',
8631 triggerEvent: false
8632 }),
8633 id: function id() {
8634 var ele = this[0];
8635
8636 if (ele) {
8637 return ele._private.data.id;
8638 }
8639 }
8640}; // aliases
8641
8642fn$1.attr = fn$1.data;
8643fn$1.removeAttr = fn$1.removeData;
8644var data$1 = elesfn$h;
8645
8646var elesfn$i = {};
8647
8648function defineDegreeFunction(callback) {
8649 return function (includeLoops) {
8650 var self = this;
8651
8652 if (includeLoops === undefined) {
8653 includeLoops = true;
8654 }
8655
8656 if (self.length === 0) {
8657 return;
8658 }
8659
8660 if (self.isNode() && !self.removed()) {
8661 var degree = 0;
8662 var node = self[0];
8663 var connectedEdges = node._private.edges;
8664
8665 for (var i = 0; i < connectedEdges.length; i++) {
8666 var edge = connectedEdges[i];
8667
8668 if (!includeLoops && edge.isLoop()) {
8669 continue;
8670 }
8671
8672 degree += callback(node, edge);
8673 }
8674
8675 return degree;
8676 } else {
8677 return;
8678 }
8679 };
8680}
8681
8682extend(elesfn$i, {
8683 degree: defineDegreeFunction(function (node, edge) {
8684 if (edge.source().same(edge.target())) {
8685 return 2;
8686 } else {
8687 return 1;
8688 }
8689 }),
8690 indegree: defineDegreeFunction(function (node, edge) {
8691 if (edge.target().same(node)) {
8692 return 1;
8693 } else {
8694 return 0;
8695 }
8696 }),
8697 outdegree: defineDegreeFunction(function (node, edge) {
8698 if (edge.source().same(node)) {
8699 return 1;
8700 } else {
8701 return 0;
8702 }
8703 })
8704});
8705
8706function defineDegreeBoundsFunction(degreeFn, callback) {
8707 return function (includeLoops) {
8708 var ret;
8709 var nodes = this.nodes();
8710
8711 for (var i = 0; i < nodes.length; i++) {
8712 var ele = nodes[i];
8713 var degree = ele[degreeFn](includeLoops);
8714
8715 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
8716 ret = degree;
8717 }
8718 }
8719
8720 return ret;
8721 };
8722}
8723
8724extend(elesfn$i, {
8725 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
8726 return degree < min;
8727 }),
8728 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
8729 return degree > max;
8730 }),
8731 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
8732 return degree < min;
8733 }),
8734 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
8735 return degree > max;
8736 }),
8737 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
8738 return degree < min;
8739 }),
8740 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
8741 return degree > max;
8742 })
8743});
8744extend(elesfn$i, {
8745 totalDegree: function totalDegree(includeLoops) {
8746 var total = 0;
8747 var nodes = this.nodes();
8748
8749 for (var i = 0; i < nodes.length; i++) {
8750 total += nodes[i].degree(includeLoops);
8751 }
8752
8753 return total;
8754 }
8755});
8756
8757var fn$2, elesfn$j;
8758
8759var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
8760 for (var i = 0; i < eles.length; i++) {
8761 var ele = eles[i];
8762
8763 if (!ele.locked()) {
8764 var oldPos = ele._private.position;
8765 var delta = {
8766 x: newPos.x != null ? newPos.x - oldPos.x : 0,
8767 y: newPos.y != null ? newPos.y - oldPos.y : 0
8768 };
8769
8770 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
8771 ele.children().shift(delta, silent);
8772 }
8773
8774 ele.dirtyBoundingBoxCache();
8775 }
8776 }
8777};
8778
8779var positionDef = {
8780 field: 'position',
8781 bindingEvent: 'position',
8782 allowBinding: true,
8783 allowSetting: true,
8784 settingEvent: 'position',
8785 settingTriggersEvent: true,
8786 triggerFnName: 'emitAndNotify',
8787 allowGetting: true,
8788 validKeys: ['x', 'y'],
8789 beforeGet: function beforeGet(ele) {
8790 ele.updateCompoundBounds();
8791 },
8792 beforeSet: function beforeSet(eles, newPos) {
8793 beforePositionSet(eles, newPos, false);
8794 },
8795 onSet: function onSet(eles) {
8796 eles.dirtyCompoundBoundsCache();
8797 },
8798 canSet: function canSet(ele) {
8799 return !ele.locked();
8800 }
8801};
8802fn$2 = elesfn$j = {
8803 position: define$3.data(positionDef),
8804 // position but no notification to renderer
8805 silentPosition: define$3.data(extend({}, positionDef, {
8806 allowBinding: false,
8807 allowSetting: true,
8808 settingTriggersEvent: false,
8809 allowGetting: false,
8810 beforeSet: function beforeSet(eles, newPos) {
8811 beforePositionSet(eles, newPos, true);
8812 },
8813 onSet: function onSet(eles) {
8814 eles.dirtyCompoundBoundsCache();
8815 }
8816 })),
8817 positions: function positions(pos, silent) {
8818 if (plainObject(pos)) {
8819 if (silent) {
8820 this.silentPosition(pos);
8821 } else {
8822 this.position(pos);
8823 }
8824 } else if (fn(pos)) {
8825 var _fn = pos;
8826 var cy = this.cy();
8827 cy.startBatch();
8828
8829 for (var i = 0; i < this.length; i++) {
8830 var ele = this[i];
8831
8832 var _pos = void 0;
8833
8834 if (_pos = _fn(ele, i)) {
8835 if (silent) {
8836 ele.silentPosition(_pos);
8837 } else {
8838 ele.position(_pos);
8839 }
8840 }
8841 }
8842
8843 cy.endBatch();
8844 }
8845
8846 return this; // chaining
8847 },
8848 silentPositions: function silentPositions(pos) {
8849 return this.positions(pos, true);
8850 },
8851 shift: function shift(dim, val, silent) {
8852 var delta;
8853
8854 if (plainObject(dim)) {
8855 delta = {
8856 x: number(dim.x) ? dim.x : 0,
8857 y: number(dim.y) ? dim.y : 0
8858 };
8859 silent = val;
8860 } else if (string(dim) && number(val)) {
8861 delta = {
8862 x: 0,
8863 y: 0
8864 };
8865 delta[dim] = val;
8866 }
8867
8868 if (delta != null) {
8869 var cy = this.cy();
8870 cy.startBatch();
8871
8872 for (var i = 0; i < this.length; i++) {
8873 var ele = this[i];
8874 var pos = ele.position();
8875 var newPos = {
8876 x: pos.x + delta.x,
8877 y: pos.y + delta.y
8878 };
8879
8880 if (silent) {
8881 ele.silentPosition(newPos);
8882 } else {
8883 ele.position(newPos);
8884 }
8885 }
8886
8887 cy.endBatch();
8888 }
8889
8890 return this;
8891 },
8892 silentShift: function silentShift(dim, val) {
8893 if (plainObject(dim)) {
8894 this.shift(dim, true);
8895 } else if (string(dim) && number(val)) {
8896 this.shift(dim, val, true);
8897 }
8898
8899 return this;
8900 },
8901 // get/set the rendered (i.e. on screen) positon of the element
8902 renderedPosition: function renderedPosition(dim, val) {
8903 var ele = this[0];
8904 var cy = this.cy();
8905 var zoom = cy.zoom();
8906 var pan = cy.pan();
8907 var rpos = plainObject(dim) ? dim : undefined;
8908 var setting = rpos !== undefined || val !== undefined && string(dim);
8909
8910 if (ele && ele.isNode()) {
8911 // must have an element and must be a node to return position
8912 if (setting) {
8913 for (var i = 0; i < this.length; i++) {
8914 var _ele = this[i];
8915
8916 if (val !== undefined) {
8917 // set one dimension
8918 _ele.position(dim, (val - pan[dim]) / zoom);
8919 } else if (rpos !== undefined) {
8920 // set whole position
8921 _ele.position(renderedToModelPosition(rpos, zoom, pan));
8922 }
8923 }
8924 } else {
8925 // getting
8926 var pos = ele.position();
8927 rpos = modelToRenderedPosition(pos, zoom, pan);
8928
8929 if (dim === undefined) {
8930 // then return the whole rendered position
8931 return rpos;
8932 } else {
8933 // then return the specified dimension
8934 return rpos[dim];
8935 }
8936 }
8937 } else if (!setting) {
8938 return undefined; // for empty collection case
8939 }
8940
8941 return this; // chaining
8942 },
8943 // get/set the position relative to the parent
8944 relativePosition: function relativePosition(dim, val) {
8945 var ele = this[0];
8946 var cy = this.cy();
8947 var ppos = plainObject(dim) ? dim : undefined;
8948 var setting = ppos !== undefined || val !== undefined && string(dim);
8949 var hasCompoundNodes = cy.hasCompoundNodes();
8950
8951 if (ele && ele.isNode()) {
8952 // must have an element and must be a node to return position
8953 if (setting) {
8954 for (var i = 0; i < this.length; i++) {
8955 var _ele2 = this[i];
8956 var parent = hasCompoundNodes ? _ele2.parent() : null;
8957 var hasParent = parent && parent.length > 0;
8958 var relativeToParent = hasParent;
8959
8960 if (hasParent) {
8961 parent = parent[0];
8962 }
8963
8964 var origin = relativeToParent ? parent.position() : {
8965 x: 0,
8966 y: 0
8967 };
8968
8969 if (val !== undefined) {
8970 // set one dimension
8971 _ele2.position(dim, val + origin[dim]);
8972 } else if (ppos !== undefined) {
8973 // set whole position
8974 _ele2.position({
8975 x: ppos.x + origin.x,
8976 y: ppos.y + origin.y
8977 });
8978 }
8979 }
8980 } else {
8981 // getting
8982 var pos = ele.position();
8983
8984 var _parent = hasCompoundNodes ? ele.parent() : null;
8985
8986 var _hasParent = _parent && _parent.length > 0;
8987
8988 var _relativeToParent = _hasParent;
8989
8990 if (_hasParent) {
8991 _parent = _parent[0];
8992 }
8993
8994 var _origin = _relativeToParent ? _parent.position() : {
8995 x: 0,
8996 y: 0
8997 };
8998
8999 ppos = {
9000 x: pos.x - _origin.x,
9001 y: pos.y - _origin.y
9002 };
9003
9004 if (dim === undefined) {
9005 // then return the whole rendered position
9006 return ppos;
9007 } else {
9008 // then return the specified dimension
9009 return ppos[dim];
9010 }
9011 }
9012 } else if (!setting) {
9013 return undefined; // for empty collection case
9014 }
9015
9016 return this; // chaining
9017 }
9018}; // aliases
9019
9020fn$2.modelPosition = fn$2.point = fn$2.position;
9021fn$2.modelPositions = fn$2.points = fn$2.positions;
9022fn$2.renderedPoint = fn$2.renderedPosition;
9023fn$2.relativePoint = fn$2.relativePosition;
9024var position = elesfn$j;
9025
9026var fn$3, elesfn$k;
9027fn$3 = elesfn$k = {};
9028
9029elesfn$k.renderedBoundingBox = function (options) {
9030 var bb = this.boundingBox(options);
9031 var cy = this.cy();
9032 var zoom = cy.zoom();
9033 var pan = cy.pan();
9034 var x1 = bb.x1 * zoom + pan.x;
9035 var x2 = bb.x2 * zoom + pan.x;
9036 var y1 = bb.y1 * zoom + pan.y;
9037 var y2 = bb.y2 * zoom + pan.y;
9038 return {
9039 x1: x1,
9040 x2: x2,
9041 y1: y1,
9042 y2: y2,
9043 w: x2 - x1,
9044 h: y2 - y1
9045 };
9046};
9047
9048elesfn$k.dirtyCompoundBoundsCache = function () {
9049 var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9050 var cy = this.cy();
9051
9052 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9053 return this;
9054 }
9055
9056 this.forEachUp(function (ele) {
9057 if (ele.isParent()) {
9058 var _p = ele._private;
9059 _p.compoundBoundsClean = false;
9060 _p.bbCache = null;
9061
9062 if (!silent) {
9063 ele.emitAndNotify('bounds');
9064 }
9065 }
9066 });
9067 return this;
9068};
9069
9070elesfn$k.updateCompoundBounds = function () {
9071 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9072 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9073
9074 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9075 return this;
9076 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9077
9078
9079 if (!force && cy.batching()) {
9080 return this;
9081 }
9082
9083 function update(parent) {
9084 if (!parent.isParent()) {
9085 return;
9086 }
9087
9088 var _p = parent._private;
9089 var children = parent.children();
9090 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9091 var min = {
9092 width: {
9093 val: parent.pstyle('min-width').pfValue,
9094 left: parent.pstyle('min-width-bias-left'),
9095 right: parent.pstyle('min-width-bias-right')
9096 },
9097 height: {
9098 val: parent.pstyle('min-height').pfValue,
9099 top: parent.pstyle('min-height-bias-top'),
9100 bottom: parent.pstyle('min-height-bias-bottom')
9101 }
9102 };
9103 var bb = children.boundingBox({
9104 includeLabels: includeLabels,
9105 includeOverlays: false,
9106 // updating the compound bounds happens outside of the regular
9107 // cache cycle (i.e. before fired events)
9108 useCache: false
9109 });
9110 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9111
9112 if (bb.w === 0 || bb.h === 0) {
9113 bb = {
9114 w: parent.pstyle('width').pfValue,
9115 h: parent.pstyle('height').pfValue
9116 };
9117 bb.x1 = pos.x - bb.w / 2;
9118 bb.x2 = pos.x + bb.w / 2;
9119 bb.y1 = pos.y - bb.h / 2;
9120 bb.y2 = pos.y + bb.h / 2;
9121 }
9122
9123 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9124 var biasDiff = 0;
9125 var biasComplementDiff = 0;
9126 var biasTotal = propBias + propBiasComplement;
9127
9128 if (propDiff > 0 && biasTotal > 0) {
9129 biasDiff = propBias / biasTotal * propDiff;
9130 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9131 }
9132
9133 return {
9134 biasDiff: biasDiff,
9135 biasComplementDiff: biasComplementDiff
9136 };
9137 }
9138
9139 function computePaddingValues(width, height, paddingObject, relativeTo) {
9140 // Assuming percentage is number from 0 to 1
9141 if (paddingObject.units === '%') {
9142 switch (relativeTo) {
9143 case 'width':
9144 return width > 0 ? paddingObject.pfValue * width : 0;
9145
9146 case 'height':
9147 return height > 0 ? paddingObject.pfValue * height : 0;
9148
9149 case 'average':
9150 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9151
9152 case 'min':
9153 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9154
9155 case 'max':
9156 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9157
9158 default:
9159 return 0;
9160 }
9161 } else if (paddingObject.units === 'px') {
9162 return paddingObject.pfValue;
9163 } else {
9164 return 0;
9165 }
9166 }
9167
9168 var leftVal = min.width.left.value;
9169
9170 if (min.width.left.units === 'px' && min.width.val > 0) {
9171 leftVal = leftVal * 100 / min.width.val;
9172 }
9173
9174 var rightVal = min.width.right.value;
9175
9176 if (min.width.right.units === 'px' && min.width.val > 0) {
9177 rightVal = rightVal * 100 / min.width.val;
9178 }
9179
9180 var topVal = min.height.top.value;
9181
9182 if (min.height.top.units === 'px' && min.height.val > 0) {
9183 topVal = topVal * 100 / min.height.val;
9184 }
9185
9186 var bottomVal = min.height.bottom.value;
9187
9188 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9189 bottomVal = bottomVal * 100 / min.height.val;
9190 }
9191
9192 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9193 var diffLeft = widthBiasDiffs.biasDiff;
9194 var diffRight = widthBiasDiffs.biasComplementDiff;
9195 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9196 var diffTop = heightBiasDiffs.biasDiff;
9197 var diffBottom = heightBiasDiffs.biasComplementDiff;
9198 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9199 _p.autoWidth = Math.max(bb.w, min.width.val);
9200 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9201 _p.autoHeight = Math.max(bb.h, min.height.val);
9202 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9203 }
9204
9205 for (var i = 0; i < this.length; i++) {
9206 var ele = this[i];
9207 var _p = ele._private;
9208
9209 if (!_p.compoundBoundsClean || force) {
9210 update(ele);
9211
9212 if (!cy.batching()) {
9213 _p.compoundBoundsClean = true;
9214 }
9215 }
9216 }
9217
9218 return this;
9219};
9220
9221var noninf = function noninf(x) {
9222 if (x === Infinity || x === -Infinity) {
9223 return 0;
9224 }
9225
9226 return x;
9227};
9228
9229var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9230 // don't update with zero area boxes
9231 if (x2 - x1 === 0 || y2 - y1 === 0) {
9232 return;
9233 } // don't update with null dim
9234
9235
9236 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9237 return;
9238 }
9239
9240 b.x1 = x1 < b.x1 ? x1 : b.x1;
9241 b.x2 = x2 > b.x2 ? x2 : b.x2;
9242 b.y1 = y1 < b.y1 ? y1 : b.y1;
9243 b.y2 = y2 > b.y2 ? y2 : b.y2;
9244 b.w = b.x2 - b.x1;
9245 b.h = b.y2 - b.y1;
9246};
9247
9248var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
9249 if (b2 == null) {
9250 return b;
9251 }
9252
9253 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
9254};
9255
9256var prefixedProperty = function prefixedProperty(obj, field, prefix) {
9257 return getPrefixedProperty(obj, field, prefix);
9258};
9259
9260var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
9261 if (ele.cy().headless()) {
9262 return;
9263 }
9264
9265 var _p = ele._private;
9266 var rstyle = _p.rstyle;
9267 var halfArW = rstyle.arrowWidth / 2;
9268 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
9269 var x;
9270 var y;
9271
9272 if (arrowType !== 'none') {
9273 if (prefix === 'source') {
9274 x = rstyle.srcX;
9275 y = rstyle.srcY;
9276 } else if (prefix === 'target') {
9277 x = rstyle.tgtX;
9278 y = rstyle.tgtY;
9279 } else {
9280 x = rstyle.midX;
9281 y = rstyle.midY;
9282 } // always store the individual arrow bounds
9283
9284
9285 var bbs = _p.arrowBounds = _p.arrowBounds || {};
9286 var bb = bbs[prefix] = bbs[prefix] || {};
9287 bb.x1 = x - halfArW;
9288 bb.y1 = y - halfArW;
9289 bb.x2 = x + halfArW;
9290 bb.y2 = y + halfArW;
9291 bb.w = bb.x2 - bb.x1;
9292 bb.h = bb.y2 - bb.y1;
9293 expandBoundingBox(bb, 1);
9294 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
9295 }
9296};
9297
9298var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
9299 if (ele.cy().headless()) {
9300 return;
9301 }
9302
9303 var prefixDash;
9304
9305 if (prefix) {
9306 prefixDash = prefix + '-';
9307 } else {
9308 prefixDash = '';
9309 }
9310
9311 var _p = ele._private;
9312 var rstyle = _p.rstyle;
9313 var label = ele.pstyle(prefixDash + 'label').strValue;
9314
9315 if (label) {
9316 var halign = ele.pstyle('text-halign');
9317 var valign = ele.pstyle('text-valign');
9318 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
9319 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
9320 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
9321 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
9322 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
9323 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
9324 var isEdge = ele.isEdge();
9325 var rotation = ele.pstyle(prefixDash + 'text-rotation');
9326 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
9327 var borderWidth = ele.pstyle('text-border-width').pfValue;
9328 var halfBorderWidth = borderWidth / 2;
9329 var padding = ele.pstyle('text-background-padding').pfValue;
9330 var marginOfError = 2; // expand to work around browser dimension inaccuracies
9331
9332 var lh = labelHeight;
9333 var lw = labelWidth;
9334 var lw_2 = lw / 2;
9335 var lh_2 = lh / 2;
9336 var lx1, lx2, ly1, ly2;
9337
9338 if (isEdge) {
9339 lx1 = labelX - lw_2;
9340 lx2 = labelX + lw_2;
9341 ly1 = labelY - lh_2;
9342 ly2 = labelY + lh_2;
9343 } else {
9344 switch (halign.value) {
9345 case 'left':
9346 lx1 = labelX - lw;
9347 lx2 = labelX;
9348 break;
9349
9350 case 'center':
9351 lx1 = labelX - lw_2;
9352 lx2 = labelX + lw_2;
9353 break;
9354
9355 case 'right':
9356 lx1 = labelX;
9357 lx2 = labelX + lw;
9358 break;
9359 }
9360
9361 switch (valign.value) {
9362 case 'top':
9363 ly1 = labelY - lh;
9364 ly2 = labelY;
9365 break;
9366
9367 case 'center':
9368 ly1 = labelY - lh_2;
9369 ly2 = labelY + lh_2;
9370 break;
9371
9372 case 'bottom':
9373 ly1 = labelY;
9374 ly2 = labelY + lh;
9375 break;
9376 }
9377 } // shift by margin and expand by outline and border
9378
9379
9380 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9381 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
9382 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9383 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError; // always store the unrotated label bounds separately
9384
9385 var bbPrefix = prefix || 'main';
9386 var bbs = _p.labelBounds;
9387 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
9388 bb.x1 = lx1;
9389 bb.y1 = ly1;
9390 bb.x2 = lx2;
9391 bb.y2 = ly2;
9392 bb.w = lx2 - lx1;
9393 bb.h = ly2 - ly1;
9394 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
9395 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
9396
9397 if (isAutorotate || isPfValue) {
9398 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
9399 var cos = Math.cos(theta);
9400 var sin = Math.sin(theta); // rotation point (default value for center-center)
9401
9402 var xo = (lx1 + lx2) / 2;
9403 var yo = (ly1 + ly2) / 2;
9404
9405 if (!isEdge) {
9406 switch (halign.value) {
9407 case 'left':
9408 xo = lx2;
9409 break;
9410
9411 case 'right':
9412 xo = lx1;
9413 break;
9414 }
9415
9416 switch (valign.value) {
9417 case 'top':
9418 yo = ly2;
9419 break;
9420
9421 case 'bottom':
9422 yo = ly1;
9423 break;
9424 }
9425 }
9426
9427 var rotate = function rotate(x, y) {
9428 x = x - xo;
9429 y = y - yo;
9430 return {
9431 x: x * cos - y * sin + xo,
9432 y: x * sin + y * cos + yo
9433 };
9434 };
9435
9436 var px1y1 = rotate(lx1, ly1);
9437 var px1y2 = rotate(lx1, ly2);
9438 var px2y1 = rotate(lx2, ly1);
9439 var px2y2 = rotate(lx2, ly2);
9440 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9441 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9442 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9443 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9444 }
9445
9446 var bbPrefixRot = bbPrefix + 'Rot';
9447 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
9448 bbRot.x1 = lx1;
9449 bbRot.y1 = ly1;
9450 bbRot.x2 = lx2;
9451 bbRot.y2 = ly2;
9452 bbRot.w = lx2 - lx1;
9453 bbRot.h = ly2 - ly1;
9454 updateBounds(bounds, lx1, ly1, lx2, ly2);
9455 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
9456 }
9457
9458 return bounds;
9459}; // get the bounding box of the elements (in raw model position)
9460
9461
9462var boundingBoxImpl = function boundingBoxImpl(ele, options) {
9463 var cy = ele._private.cy;
9464 var styleEnabled = cy.styleEnabled();
9465 var headless = cy.headless();
9466 var bounds = makeBoundingBox();
9467 var _p = ele._private;
9468 var isNode = ele.isNode();
9469 var isEdge = ele.isEdge();
9470 var ex1, ex2, ey1, ey2; // extrema of body / lines
9471
9472 var x, y; // node pos
9473
9474 var rstyle = _p.rstyle;
9475 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
9476 // (other factors like width values will be considered later in this function anyway)
9477
9478 var isDisplayed = function isDisplayed(ele) {
9479 return ele.pstyle('display').value !== 'none';
9480 };
9481
9482 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
9483 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
9484
9485 if (displayed) {
9486 // displayed suffices, since we will find zero area eles anyway
9487 var overlayOpacity = 0;
9488 var overlayPadding = 0;
9489
9490 if (styleEnabled && options.includeOverlays) {
9491 overlayOpacity = ele.pstyle('overlay-opacity').value;
9492
9493 if (overlayOpacity !== 0) {
9494 overlayPadding = ele.pstyle('overlay-padding').value;
9495 }
9496 }
9497
9498 var w = 0;
9499 var wHalf = 0;
9500
9501 if (styleEnabled) {
9502 w = ele.pstyle('width').pfValue;
9503 wHalf = w / 2;
9504 }
9505
9506 if (isNode && options.includeNodes) {
9507 var pos = ele.position();
9508 x = pos.x;
9509 y = pos.y;
9510
9511 var _w = ele.outerWidth();
9512
9513 var halfW = _w / 2;
9514 var h = ele.outerHeight();
9515 var halfH = h / 2; // handle node dimensions
9516 /////////////////////////
9517
9518 ex1 = x - halfW;
9519 ex2 = x + halfW;
9520 ey1 = y - halfH;
9521 ey2 = y + halfH;
9522 updateBounds(bounds, ex1, ey1, ex2, ey2);
9523 } else if (isEdge && options.includeEdges) {
9524 if (styleEnabled && !headless) {
9525 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
9526 //////////////////////////////////////////////
9527
9528 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9529 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9530 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
9531 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
9532
9533 ex1 -= wHalf;
9534 ex2 += wHalf;
9535 ey1 -= wHalf;
9536 ey2 += wHalf;
9537 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
9538 ////////////////
9539
9540 if (curveStyle === 'haystack') {
9541 var hpts = rstyle.haystackPts;
9542
9543 if (hpts && hpts.length === 2) {
9544 ex1 = hpts[0].x;
9545 ey1 = hpts[0].y;
9546 ex2 = hpts[1].x;
9547 ey2 = hpts[1].y;
9548
9549 if (ex1 > ex2) {
9550 var temp = ex1;
9551 ex1 = ex2;
9552 ex2 = temp;
9553 }
9554
9555 if (ey1 > ey2) {
9556 var _temp = ey1;
9557 ey1 = ey2;
9558 ey2 = _temp;
9559 }
9560
9561 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
9562 }
9563 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
9564 var pts;
9565
9566 switch (curveStyle) {
9567 case 'bezier':
9568 case 'unbundled-bezier':
9569 pts = rstyle.bezierPts;
9570 break;
9571
9572 case 'segments':
9573 case 'taxi':
9574 pts = rstyle.linePts;
9575 break;
9576 }
9577
9578 if (pts != null) {
9579 for (var j = 0; j < pts.length; j++) {
9580 var pt = pts[j];
9581 ex1 = pt.x - wHalf;
9582 ex2 = pt.x + wHalf;
9583 ey1 = pt.y - wHalf;
9584 ey2 = pt.y + wHalf;
9585 updateBounds(bounds, ex1, ey1, ex2, ey2);
9586 }
9587 }
9588 } // bezier-like or segment-like edge
9589
9590 } else {
9591 // headless or style disabled
9592 // fallback on source and target positions
9593 //////////////////////////////////////////
9594 var n1 = ele.source();
9595 var n1pos = n1.position();
9596 var n2 = ele.target();
9597 var n2pos = n2.position();
9598 ex1 = n1pos.x;
9599 ex2 = n2pos.x;
9600 ey1 = n1pos.y;
9601 ey2 = n2pos.y;
9602
9603 if (ex1 > ex2) {
9604 var _temp2 = ex1;
9605 ex1 = ex2;
9606 ex2 = _temp2;
9607 }
9608
9609 if (ey1 > ey2) {
9610 var _temp3 = ey1;
9611 ey1 = ey2;
9612 ey2 = _temp3;
9613 } // take into account edge width
9614
9615
9616 ex1 -= wHalf;
9617 ex2 += wHalf;
9618 ey1 -= wHalf;
9619 ey2 += wHalf;
9620 updateBounds(bounds, ex1, ey1, ex2, ey2);
9621 } // headless or style disabled
9622
9623 } // edges
9624 // handle edge arrow size
9625 /////////////////////////
9626
9627
9628 if (styleEnabled && options.includeEdges && isEdge) {
9629 updateBoundsFromArrow(bounds, ele, 'mid-source');
9630 updateBoundsFromArrow(bounds, ele, 'mid-target');
9631 updateBoundsFromArrow(bounds, ele, 'source');
9632 updateBoundsFromArrow(bounds, ele, 'target');
9633 } // ghost
9634 ////////
9635
9636
9637 if (styleEnabled) {
9638 var ghost = ele.pstyle('ghost').value === 'yes';
9639
9640 if (ghost) {
9641 var gx = ele.pstyle('ghost-offset-x').pfValue;
9642 var gy = ele.pstyle('ghost-offset-y').pfValue;
9643 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
9644 }
9645 } // always store the body bounds separately from the labels
9646
9647
9648 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
9649 assignBoundingBox(bbBody, bounds);
9650 expandBoundingBoxSides(bbBody, manualExpansion);
9651 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
9652 // overlay
9653 //////////
9654
9655 if (styleEnabled) {
9656 ex1 = bounds.x1;
9657 ex2 = bounds.x2;
9658 ey1 = bounds.y1;
9659 ey2 = bounds.y2;
9660 updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
9661 } // always store the body bounds separately from the labels
9662
9663
9664 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
9665 assignBoundingBox(bbOverlay, bounds);
9666 expandBoundingBoxSides(bbOverlay, manualExpansion);
9667 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
9668 // handle label dimensions
9669 //////////////////////////
9670
9671 var bbLabels = _p.labelBounds = _p.labelBounds || {};
9672
9673 if (bbLabels.all != null) {
9674 clearBoundingBox(bbLabels.all);
9675 } else {
9676 bbLabels.all = makeBoundingBox();
9677 }
9678
9679 if (styleEnabled && options.includeLabels) {
9680 if (options.includeMainLabels) {
9681 updateBoundsFromLabel(bounds, ele, null);
9682 }
9683
9684 if (isEdge) {
9685 if (options.includeSourceLabels) {
9686 updateBoundsFromLabel(bounds, ele, 'source');
9687 }
9688
9689 if (options.includeTargetLabels) {
9690 updateBoundsFromLabel(bounds, ele, 'target');
9691 }
9692 }
9693 } // style enabled for labels
9694
9695 } // if displayed
9696
9697
9698 bounds.x1 = noninf(bounds.x1);
9699 bounds.y1 = noninf(bounds.y1);
9700 bounds.x2 = noninf(bounds.x2);
9701 bounds.y2 = noninf(bounds.y2);
9702 bounds.w = noninf(bounds.x2 - bounds.x1);
9703 bounds.h = noninf(bounds.y2 - bounds.y1);
9704
9705 if (bounds.w > 0 && bounds.h > 0 && displayed) {
9706 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
9707
9708 expandBoundingBox(bounds, 1);
9709 }
9710
9711 return bounds;
9712};
9713
9714var getKey = function getKey(opts) {
9715 var i = 0;
9716
9717 var tf = function tf(val) {
9718 return (val ? 1 : 0) << i++;
9719 };
9720
9721 var key = 0;
9722 key += tf(opts.incudeNodes);
9723 key += tf(opts.includeEdges);
9724 key += tf(opts.includeLabels);
9725 key += tf(opts.includeMainLabels);
9726 key += tf(opts.includeSourceLabels);
9727 key += tf(opts.includeTargetLabels);
9728 key += tf(opts.includeOverlays);
9729 return key;
9730};
9731
9732var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
9733 if (ele.isEdge()) {
9734 var p1 = ele.source().position();
9735 var p2 = ele.target().position();
9736
9737 var r = function r(x) {
9738 return Math.round(x);
9739 };
9740
9741 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
9742 } else {
9743 return 0;
9744 }
9745};
9746
9747var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
9748 var _p = ele._private;
9749 var bb;
9750 var isEdge = ele.isEdge();
9751 var key = opts == null ? defBbOptsKey : getKey(opts);
9752 var usingDefOpts = key === defBbOptsKey;
9753 var currPosKey = getBoundingBoxPosKey(ele);
9754 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9755 var useCache = opts.useCache && isPosKeySame;
9756
9757 var isDirty = function isDirty(ele) {
9758 return ele._private.bbCache == null || ele._private.styleDirty;
9759 };
9760
9761 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
9762
9763 if (needRecalc) {
9764 if (!isPosKeySame) {
9765 ele.recalculateRenderedStyle(useCache);
9766 }
9767
9768 bb = boundingBoxImpl(ele, defBbOpts);
9769 _p.bbCache = bb;
9770 _p.bbCachePosKey = currPosKey;
9771 } else {
9772 bb = _p.bbCache;
9773 } // not using def opts => need to build up bb from combination of sub bbs
9774
9775
9776 if (!usingDefOpts) {
9777 var isNode = ele.isNode();
9778 bb = makeBoundingBox();
9779
9780 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
9781 if (opts.includeOverlays) {
9782 updateBoundsFromBox(bb, _p.overlayBounds);
9783 } else {
9784 updateBoundsFromBox(bb, _p.bodyBounds);
9785 }
9786 }
9787
9788 if (opts.includeLabels) {
9789 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
9790 updateBoundsFromBox(bb, _p.labelBounds.all);
9791 } else {
9792 if (opts.includeMainLabels) {
9793 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
9794 }
9795
9796 if (opts.includeSourceLabels) {
9797 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
9798 }
9799
9800 if (opts.includeTargetLabels) {
9801 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
9802 }
9803 }
9804 }
9805
9806 bb.w = bb.x2 - bb.x1;
9807 bb.h = bb.y2 - bb.y1;
9808 }
9809
9810 return bb;
9811};
9812
9813var defBbOpts = {
9814 includeNodes: true,
9815 includeEdges: true,
9816 includeLabels: true,
9817 includeMainLabels: true,
9818 includeSourceLabels: true,
9819 includeTargetLabels: true,
9820 includeOverlays: true,
9821 useCache: true
9822};
9823var defBbOptsKey = getKey(defBbOpts);
9824var filledBbOpts = defaults(defBbOpts);
9825
9826elesfn$k.boundingBox = function (options) {
9827 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
9828 // specified s.t. the cache is used, so check for this case to make it faster by
9829 // avoiding the overhead of the rest of the function
9830
9831 if (this.length === 1 && this[0]._private.bbCache != null && !this[0]._private.styleDirty && (options === undefined || options.useCache === undefined || options.useCache === true)) {
9832 if (options === undefined) {
9833 options = defBbOpts;
9834 } else {
9835 options = filledBbOpts(options);
9836 }
9837
9838 bounds = cachedBoundingBoxImpl(this[0], options);
9839 } else {
9840 bounds = makeBoundingBox();
9841 options = options || defBbOpts;
9842 var opts = filledBbOpts(options);
9843 var eles = this;
9844 var cy = eles.cy();
9845 var styleEnabled = cy.styleEnabled();
9846
9847 if (styleEnabled) {
9848 for (var i = 0; i < eles.length; i++) {
9849 var ele = eles[i];
9850 var _p = ele._private;
9851 var currPosKey = getBoundingBoxPosKey(ele);
9852 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9853 var useCache = opts.useCache && isPosKeySame && !_p.styleDirty;
9854 ele.recalculateRenderedStyle(useCache);
9855 }
9856 }
9857
9858 this.updateCompoundBounds(!options.useCache);
9859
9860 for (var _i = 0; _i < eles.length; _i++) {
9861 var _ele = eles[_i];
9862 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
9863 }
9864 }
9865
9866 bounds.x1 = noninf(bounds.x1);
9867 bounds.y1 = noninf(bounds.y1);
9868 bounds.x2 = noninf(bounds.x2);
9869 bounds.y2 = noninf(bounds.y2);
9870 bounds.w = noninf(bounds.x2 - bounds.x1);
9871 bounds.h = noninf(bounds.y2 - bounds.y1);
9872 return bounds;
9873};
9874
9875elesfn$k.dirtyBoundingBoxCache = function () {
9876 for (var i = 0; i < this.length; i++) {
9877 var _p = this[i]._private;
9878 _p.bbCache = null;
9879 _p.bbCachePosKey = null;
9880 _p.bodyBounds = null;
9881 _p.overlayBounds = null;
9882 _p.labelBounds.all = null;
9883 _p.labelBounds.source = null;
9884 _p.labelBounds.target = null;
9885 _p.labelBounds.main = null;
9886 _p.labelBounds.sourceRot = null;
9887 _p.labelBounds.targetRot = null;
9888 _p.labelBounds.mainRot = null;
9889 _p.arrowBounds.source = null;
9890 _p.arrowBounds.target = null;
9891 _p.arrowBounds['mid-source'] = null;
9892 _p.arrowBounds['mid-target'] = null;
9893 }
9894
9895 this.emitAndNotify('bounds');
9896 return this;
9897}; // private helper to get bounding box for custom node positions
9898// - good for perf in certain cases but currently requires dirtying the rendered style
9899// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
9900// - try to use for only things like discrete layouts where the node position would change anyway
9901
9902
9903elesfn$k.boundingBoxAt = function (fn) {
9904 var nodes = this.nodes();
9905 var cy = this.cy();
9906 var hasCompoundNodes = cy.hasCompoundNodes();
9907 var parents = cy.collection();
9908
9909 if (hasCompoundNodes) {
9910 parents = nodes.filter(function (node) {
9911 return node.isParent();
9912 });
9913 nodes = nodes.not(parents);
9914 }
9915
9916 if (plainObject(fn)) {
9917 var obj = fn;
9918
9919 fn = function fn() {
9920 return obj;
9921 };
9922 }
9923
9924 var storeOldPos = function storeOldPos(node, i) {
9925 return node._private.bbAtOldPos = fn(node, i);
9926 };
9927
9928 var getOldPos = function getOldPos(node) {
9929 return node._private.bbAtOldPos;
9930 };
9931
9932 cy.startBatch();
9933 nodes.forEach(storeOldPos).silentPositions(fn);
9934
9935 if (hasCompoundNodes) {
9936 parents.dirtyCompoundBoundsCache();
9937 parents.dirtyBoundingBoxCache();
9938 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
9939 }
9940
9941 var bb = copyBoundingBox(this.boundingBox({
9942 useCache: false
9943 }));
9944 nodes.silentPositions(getOldPos);
9945
9946 if (hasCompoundNodes) {
9947 parents.dirtyCompoundBoundsCache();
9948 parents.dirtyBoundingBoxCache();
9949 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
9950 }
9951
9952 cy.endBatch();
9953 return bb;
9954};
9955
9956fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
9957fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
9958var bounds = elesfn$k;
9959
9960var fn$4, elesfn$l;
9961fn$4 = elesfn$l = {};
9962
9963var defineDimFns = function defineDimFns(opts) {
9964 opts.uppercaseName = capitalize(opts.name);
9965 opts.autoName = 'auto' + opts.uppercaseName;
9966 opts.labelName = 'label' + opts.uppercaseName;
9967 opts.outerName = 'outer' + opts.uppercaseName;
9968 opts.uppercaseOuterName = capitalize(opts.outerName);
9969
9970 fn$4[opts.name] = function dimImpl() {
9971 var ele = this[0];
9972 var _p = ele._private;
9973 var cy = _p.cy;
9974 var styleEnabled = cy._private.styleEnabled;
9975
9976 if (ele) {
9977 if (styleEnabled) {
9978 if (ele.isParent()) {
9979 ele.updateCompoundBounds();
9980 return _p[opts.autoName] || 0;
9981 }
9982
9983 var d = ele.pstyle(opts.name);
9984
9985 switch (d.strValue) {
9986 case 'label':
9987 ele.recalculateRenderedStyle();
9988 return _p.rstyle[opts.labelName] || 0;
9989
9990 default:
9991 return d.pfValue;
9992 }
9993 } else {
9994 return 1;
9995 }
9996 }
9997 };
9998
9999 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
10000 var ele = this[0];
10001 var _p = ele._private;
10002 var cy = _p.cy;
10003 var styleEnabled = cy._private.styleEnabled;
10004
10005 if (ele) {
10006 if (styleEnabled) {
10007 var dim = ele[opts.name]();
10008 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10009
10010 var padding = 2 * ele.padding();
10011 return dim + border + padding;
10012 } else {
10013 return 1;
10014 }
10015 }
10016 };
10017
10018 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10019 var ele = this[0];
10020
10021 if (ele) {
10022 var d = ele[opts.name]();
10023 return d * this.cy().zoom();
10024 }
10025 };
10026
10027 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10028 var ele = this[0];
10029
10030 if (ele) {
10031 var od = ele[opts.outerName]();
10032 return od * this.cy().zoom();
10033 }
10034 };
10035};
10036
10037defineDimFns({
10038 name: 'width'
10039});
10040defineDimFns({
10041 name: 'height'
10042});
10043
10044elesfn$l.padding = function () {
10045 var ele = this[0];
10046 var _p = ele._private;
10047
10048 if (ele.isParent()) {
10049 ele.updateCompoundBounds();
10050
10051 if (_p.autoPadding !== undefined) {
10052 return _p.autoPadding;
10053 } else {
10054 return ele.pstyle('padding').pfValue;
10055 }
10056 } else {
10057 return ele.pstyle('padding').pfValue;
10058 }
10059};
10060
10061elesfn$l.paddedHeight = function () {
10062 var ele = this[0];
10063 return ele.height() + 2 * ele.padding();
10064};
10065
10066elesfn$l.paddedWidth = function () {
10067 var ele = this[0];
10068 return ele.width() + 2 * ele.padding();
10069};
10070
10071var widthHeight = elesfn$l;
10072
10073var ifEdge = function ifEdge(ele, getValue) {
10074 if (ele.isEdge()) {
10075 return getValue(ele);
10076 }
10077};
10078
10079var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10080 if (ele.isEdge()) {
10081 var cy = ele.cy();
10082 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10083 }
10084};
10085
10086var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10087 if (ele.isEdge()) {
10088 var cy = ele.cy();
10089 var pan = cy.pan();
10090 var zoom = cy.zoom();
10091 return getPoints(ele).map(function (p) {
10092 return modelToRenderedPosition(p, zoom, pan);
10093 });
10094 }
10095};
10096
10097var controlPoints = function controlPoints(ele) {
10098 return ele.renderer().getControlPoints(ele);
10099};
10100
10101var segmentPoints = function segmentPoints(ele) {
10102 return ele.renderer().getSegmentPoints(ele);
10103};
10104
10105var sourceEndpoint = function sourceEndpoint(ele) {
10106 return ele.renderer().getSourceEndpoint(ele);
10107};
10108
10109var targetEndpoint = function targetEndpoint(ele) {
10110 return ele.renderer().getTargetEndpoint(ele);
10111};
10112
10113var midpoint = function midpoint(ele) {
10114 return ele.renderer().getEdgeMidpoint(ele);
10115};
10116
10117var pts = {
10118 controlPoints: {
10119 get: controlPoints,
10120 mult: true
10121 },
10122 segmentPoints: {
10123 get: segmentPoints,
10124 mult: true
10125 },
10126 sourceEndpoint: {
10127 get: sourceEndpoint
10128 },
10129 targetEndpoint: {
10130 get: targetEndpoint
10131 },
10132 midpoint: {
10133 get: midpoint
10134 }
10135};
10136
10137var renderedName = function renderedName(name) {
10138 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10139};
10140
10141var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10142 var spec = pts[name];
10143 var rName = renderedName(name);
10144
10145 obj[name] = function () {
10146 return ifEdge(this, spec.get);
10147 };
10148
10149 if (spec.mult) {
10150 obj[rName] = function () {
10151 return ifEdgeRenderedPositions(this, spec.get);
10152 };
10153 } else {
10154 obj[rName] = function () {
10155 return ifEdgeRenderedPosition(this, spec.get);
10156 };
10157 }
10158
10159 return obj;
10160}, {});
10161
10162var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10163
10164/*!
10165Event object based on jQuery events, MIT license
10166
10167https://jquery.org/license/
10168https://tldrlegal.com/license/mit-license
10169https://github.com/jquery/jquery/blob/master/src/event.js
10170*/
10171var Event = function Event(src, props) {
10172 this.recycle(src, props);
10173};
10174
10175function returnFalse() {
10176 return false;
10177}
10178
10179function returnTrue() {
10180 return true;
10181} // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10182
10183
10184Event.prototype = {
10185 instanceString: function instanceString() {
10186 return 'event';
10187 },
10188 recycle: function recycle(src, props) {
10189 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10190
10191 if (src != null && src.preventDefault) {
10192 // Browser Event object
10193 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10194 // by a handler lower down the tree; reflect the correct value.
10195
10196 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10197 } else if (src != null && src.type) {
10198 // Plain object containing all event details
10199 props = src;
10200 } else {
10201 // Event string
10202 this.type = src;
10203 } // Put explicitly provided properties onto the event object
10204
10205
10206 if (props != null) {
10207 // more efficient to manually copy fields we use
10208 this.originalEvent = props.originalEvent;
10209 this.type = props.type != null ? props.type : this.type;
10210 this.cy = props.cy;
10211 this.target = props.target;
10212 this.position = props.position;
10213 this.renderedPosition = props.renderedPosition;
10214 this.namespace = props.namespace;
10215 this.layout = props.layout;
10216 }
10217
10218 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10219 // create a rendered position based on the passed position
10220 var pos = this.position;
10221 var zoom = this.cy.zoom();
10222 var pan = this.cy.pan();
10223 this.renderedPosition = {
10224 x: pos.x * zoom + pan.x,
10225 y: pos.y * zoom + pan.y
10226 };
10227 } // Create a timestamp if incoming event doesn't have one
10228
10229
10230 this.timeStamp = src && src.timeStamp || Date.now();
10231 },
10232 preventDefault: function preventDefault() {
10233 this.isDefaultPrevented = returnTrue;
10234 var e = this.originalEvent;
10235
10236 if (!e) {
10237 return;
10238 } // if preventDefault exists run it on the original event
10239
10240
10241 if (e.preventDefault) {
10242 e.preventDefault();
10243 }
10244 },
10245 stopPropagation: function stopPropagation() {
10246 this.isPropagationStopped = returnTrue;
10247 var e = this.originalEvent;
10248
10249 if (!e) {
10250 return;
10251 } // if stopPropagation exists run it on the original event
10252
10253
10254 if (e.stopPropagation) {
10255 e.stopPropagation();
10256 }
10257 },
10258 stopImmediatePropagation: function stopImmediatePropagation() {
10259 this.isImmediatePropagationStopped = returnTrue;
10260 this.stopPropagation();
10261 },
10262 isDefaultPrevented: returnFalse,
10263 isPropagationStopped: returnFalse,
10264 isImmediatePropagationStopped: returnFalse
10265};
10266
10267var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
10268
10269var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
10270
10271var defaults$8 = {
10272 qualifierCompare: function qualifierCompare(q1, q2) {
10273 return q1 === q2;
10274 },
10275 eventMatches: function eventMatches()
10276 /*context, listener, eventObj*/
10277 {
10278 return true;
10279 },
10280 addEventFields: function addEventFields()
10281 /*context, evt*/
10282 {},
10283 callbackContext: function callbackContext(context
10284 /*, listener, eventObj*/
10285 ) {
10286 return context;
10287 },
10288 beforeEmit: function beforeEmit()
10289 /* context, listener, eventObj */
10290 {},
10291 afterEmit: function afterEmit()
10292 /* context, listener, eventObj */
10293 {},
10294 bubble: function bubble()
10295 /*context*/
10296 {
10297 return false;
10298 },
10299 parent: function parent()
10300 /*context*/
10301 {
10302 return null;
10303 },
10304 context: null
10305};
10306var defaultsKeys = Object.keys(defaults$8);
10307var emptyOpts = {};
10308
10309function Emitter() {
10310 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
10311 var context = arguments.length > 1 ? arguments[1] : undefined;
10312
10313 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
10314 for (var i = 0; i < defaultsKeys.length; i++) {
10315 var key = defaultsKeys[i];
10316 this[key] = opts[key] || defaults$8[key];
10317 }
10318
10319 this.context = context || this.context;
10320 this.listeners = [];
10321 this.emitting = 0;
10322}
10323
10324var p = Emitter.prototype;
10325
10326var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
10327 if (fn(qualifier)) {
10328 callback = qualifier;
10329 qualifier = null;
10330 }
10331
10332 if (confOverrides) {
10333 if (conf == null) {
10334 conf = confOverrides;
10335 } else {
10336 conf = extend({}, conf, confOverrides);
10337 }
10338 }
10339
10340 var eventList = array(events) ? events : events.split(/\s+/);
10341
10342 for (var i = 0; i < eventList.length; i++) {
10343 var evt = eventList[i];
10344
10345 if (emptyString(evt)) {
10346 continue;
10347 }
10348
10349 var match = evt.match(eventRegex); // type[.namespace]
10350
10351 if (match) {
10352 var type = match[1];
10353 var namespace = match[2] ? match[2] : null;
10354 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
10355
10356 if (ret === false) {
10357 break;
10358 } // allow exiting early
10359
10360 }
10361 }
10362};
10363
10364var makeEventObj = function makeEventObj(self, obj) {
10365 self.addEventFields(self.context, obj);
10366 return new Event(obj.type, obj);
10367};
10368
10369var forEachEventObj = function forEachEventObj(self, handler, events) {
10370 if (event(events)) {
10371 handler(self, events);
10372 return;
10373 } else if (plainObject(events)) {
10374 handler(self, makeEventObj(self, events));
10375 return;
10376 }
10377
10378 var eventList = array(events) ? events : events.split(/\s+/);
10379
10380 for (var i = 0; i < eventList.length; i++) {
10381 var evt = eventList[i];
10382
10383 if (emptyString(evt)) {
10384 continue;
10385 }
10386
10387 var match = evt.match(eventRegex); // type[.namespace]
10388
10389 if (match) {
10390 var type = match[1];
10391 var namespace = match[2] ? match[2] : null;
10392 var eventObj = makeEventObj(self, {
10393 type: type,
10394 namespace: namespace,
10395 target: self.context
10396 });
10397 handler(self, eventObj);
10398 }
10399 }
10400};
10401
10402p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
10403 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
10404 if (fn(callback)) {
10405 self.listeners.push({
10406 event: event,
10407 // full event string
10408 callback: callback,
10409 // callback to run
10410 type: type,
10411 // the event type (e.g. 'click')
10412 namespace: namespace,
10413 // the event namespace (e.g. ".foo")
10414 qualifier: qualifier,
10415 // a restriction on whether to match this emitter
10416 conf: conf // additional configuration
10417
10418 });
10419 }
10420 }, events, qualifier, callback, conf, confOverrides);
10421 return this;
10422};
10423
10424p.one = function (events, qualifier, callback, conf) {
10425 return this.on(events, qualifier, callback, conf, {
10426 one: true
10427 });
10428};
10429
10430p.removeListener = p.off = function (events, qualifier, callback, conf) {
10431 var _this = this;
10432
10433 if (this.emitting !== 0) {
10434 this.listeners = copyArray(this.listeners);
10435 }
10436
10437 var listeners = this.listeners;
10438
10439 var _loop = function _loop(i) {
10440 var listener = listeners[i];
10441 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
10442 /*, conf*/
10443 ) {
10444 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
10445 listeners.splice(i, 1);
10446 return false;
10447 }
10448 }, events, qualifier, callback, conf);
10449 };
10450
10451 for (var i = listeners.length - 1; i >= 0; i--) {
10452 _loop(i);
10453 }
10454
10455 return this;
10456};
10457
10458p.removeAllListeners = function () {
10459 return this.removeListener('*');
10460};
10461
10462p.emit = p.trigger = function (events, extraParams, manualCallback) {
10463 var listeners = this.listeners;
10464 var numListenersBeforeEmit = listeners.length;
10465 this.emitting++;
10466
10467 if (!array(extraParams)) {
10468 extraParams = [extraParams];
10469 }
10470
10471 forEachEventObj(this, function (self, eventObj) {
10472 if (manualCallback != null) {
10473 listeners = [{
10474 event: eventObj.event,
10475 type: eventObj.type,
10476 namespace: eventObj.namespace,
10477 callback: manualCallback
10478 }];
10479 numListenersBeforeEmit = listeners.length;
10480 }
10481
10482 var _loop2 = function _loop2(i) {
10483 var listener = listeners[i];
10484
10485 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
10486 var args = [eventObj];
10487
10488 if (extraParams != null) {
10489 push(args, extraParams);
10490 }
10491
10492 self.beforeEmit(self.context, listener, eventObj);
10493
10494 if (listener.conf && listener.conf.one) {
10495 self.listeners = self.listeners.filter(function (l) {
10496 return l !== listener;
10497 });
10498 }
10499
10500 var context = self.callbackContext(self.context, listener, eventObj);
10501 var ret = listener.callback.apply(context, args);
10502 self.afterEmit(self.context, listener, eventObj);
10503
10504 if (ret === false) {
10505 eventObj.stopPropagation();
10506 eventObj.preventDefault();
10507 }
10508 } // if listener matches
10509
10510 };
10511
10512 for (var i = 0; i < numListenersBeforeEmit; i++) {
10513 _loop2(i);
10514 } // for listener
10515
10516
10517 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
10518 self.parent(self.context).emit(eventObj, extraParams);
10519 }
10520 }, events);
10521 this.emitting--;
10522 return this;
10523};
10524
10525var emitterOptions = {
10526 qualifierCompare: function qualifierCompare(selector1, selector2) {
10527 if (selector1 == null || selector2 == null) {
10528 return selector1 == null && selector2 == null;
10529 } else {
10530 return selector1.sameText(selector2);
10531 }
10532 },
10533 eventMatches: function eventMatches(ele, listener, eventObj) {
10534 var selector = listener.qualifier;
10535
10536 if (selector != null) {
10537 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
10538 }
10539
10540 return true;
10541 },
10542 addEventFields: function addEventFields(ele, evt) {
10543 evt.cy = ele.cy();
10544 evt.target = ele;
10545 },
10546 callbackContext: function callbackContext(ele, listener, eventObj) {
10547 return listener.qualifier != null ? eventObj.target : ele;
10548 },
10549 beforeEmit: function beforeEmit(context, listener
10550 /*, eventObj*/
10551 ) {
10552 if (listener.conf && listener.conf.once) {
10553 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
10554 }
10555 },
10556 bubble: function bubble() {
10557 return true;
10558 },
10559 parent: function parent(ele) {
10560 return ele.isChild() ? ele.parent() : ele.cy();
10561 }
10562};
10563
10564var argSelector = function argSelector(arg) {
10565 if (string(arg)) {
10566 return new Selector(arg);
10567 } else {
10568 return arg;
10569 }
10570};
10571
10572var elesfn$m = {
10573 createEmitter: function createEmitter() {
10574 for (var i = 0; i < this.length; i++) {
10575 var ele = this[i];
10576 var _p = ele._private;
10577
10578 if (!_p.emitter) {
10579 _p.emitter = new Emitter(emitterOptions, ele);
10580 }
10581 }
10582
10583 return this;
10584 },
10585 emitter: function emitter() {
10586 return this._private.emitter;
10587 },
10588 on: function on(events, selector, callback) {
10589 var argSel = argSelector(selector);
10590
10591 for (var i = 0; i < this.length; i++) {
10592 var ele = this[i];
10593 ele.emitter().on(events, argSel, callback);
10594 }
10595
10596 return this;
10597 },
10598 removeListener: function removeListener(events, selector, callback) {
10599 var argSel = argSelector(selector);
10600
10601 for (var i = 0; i < this.length; i++) {
10602 var ele = this[i];
10603 ele.emitter().removeListener(events, argSel, callback);
10604 }
10605
10606 return this;
10607 },
10608 removeAllListeners: function removeAllListeners() {
10609 for (var i = 0; i < this.length; i++) {
10610 var ele = this[i];
10611 ele.emitter().removeAllListeners();
10612 }
10613
10614 return this;
10615 },
10616 one: function one(events, selector, callback) {
10617 var argSel = argSelector(selector);
10618
10619 for (var i = 0; i < this.length; i++) {
10620 var ele = this[i];
10621 ele.emitter().one(events, argSel, callback);
10622 }
10623
10624 return this;
10625 },
10626 once: function once(events, selector, callback) {
10627 var argSel = argSelector(selector);
10628
10629 for (var i = 0; i < this.length; i++) {
10630 var ele = this[i];
10631 ele.emitter().on(events, argSel, callback, {
10632 once: true,
10633 onceCollection: this
10634 });
10635 }
10636 },
10637 emit: function emit(events, extraParams) {
10638 for (var i = 0; i < this.length; i++) {
10639 var ele = this[i];
10640 ele.emitter().emit(events, extraParams);
10641 }
10642
10643 return this;
10644 },
10645 emitAndNotify: function emitAndNotify(event, extraParams) {
10646 // for internal use only
10647 if (this.length === 0) {
10648 return;
10649 } // empty collections don't need to notify anything
10650 // notify renderer
10651
10652
10653 this.cy().notify(event, this);
10654 this.emit(event, extraParams);
10655 return this;
10656 }
10657};
10658define$3.eventAliasesOn(elesfn$m);
10659
10660var elesfn$n = {
10661 nodes: function nodes(selector) {
10662 return this.filter(function (ele) {
10663 return ele.isNode();
10664 }).filter(selector);
10665 },
10666 edges: function edges(selector) {
10667 return this.filter(function (ele) {
10668 return ele.isEdge();
10669 }).filter(selector);
10670 },
10671 // internal helper to get nodes and edges as separate collections with single iteration over elements
10672 byGroup: function byGroup() {
10673 var nodes = this.spawn();
10674 var edges = this.spawn();
10675
10676 for (var i = 0; i < this.length; i++) {
10677 var ele = this[i];
10678
10679 if (ele.isNode()) {
10680 nodes.push(ele);
10681 } else {
10682 edges.push(ele);
10683 }
10684 }
10685
10686 return {
10687 nodes: nodes,
10688 edges: edges
10689 };
10690 },
10691 filter: function filter(_filter, thisArg) {
10692 if (_filter === undefined) {
10693 // check this first b/c it's the most common/performant case
10694 return this;
10695 } else if (string(_filter) || elementOrCollection(_filter)) {
10696 return new Selector(_filter).filter(this);
10697 } else if (fn(_filter)) {
10698 var filterEles = this.spawn();
10699 var eles = this;
10700
10701 for (var i = 0; i < eles.length; i++) {
10702 var ele = eles[i];
10703 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
10704
10705 if (include) {
10706 filterEles.push(ele);
10707 }
10708 }
10709
10710 return filterEles;
10711 }
10712
10713 return this.spawn(); // if not handled by above, give 'em an empty collection
10714 },
10715 not: function not(toRemove) {
10716 if (!toRemove) {
10717 return this;
10718 } else {
10719 if (string(toRemove)) {
10720 toRemove = this.filter(toRemove);
10721 }
10722
10723 var elements = this.spawn();
10724
10725 for (var i = 0; i < this.length; i++) {
10726 var element = this[i];
10727 var remove = toRemove.has(element);
10728
10729 if (!remove) {
10730 elements.push(element);
10731 }
10732 }
10733
10734 return elements;
10735 }
10736 },
10737 absoluteComplement: function absoluteComplement() {
10738 var cy = this.cy();
10739 return cy.mutableElements().not(this);
10740 },
10741 intersect: function intersect(other) {
10742 // if a selector is specified, then filter by it instead
10743 if (string(other)) {
10744 var selector = other;
10745 return this.filter(selector);
10746 }
10747
10748 var elements = this.spawn();
10749 var col1 = this;
10750 var col2 = other;
10751 var col1Smaller = this.length < other.length;
10752 var colS = col1Smaller ? col1 : col2;
10753 var colL = col1Smaller ? col2 : col1;
10754
10755 for (var i = 0; i < colS.length; i++) {
10756 var ele = colS[i];
10757
10758 if (colL.has(ele)) {
10759 elements.push(ele);
10760 }
10761 }
10762
10763 return elements;
10764 },
10765 xor: function xor(other) {
10766 var cy = this._private.cy;
10767
10768 if (string(other)) {
10769 other = cy.$(other);
10770 }
10771
10772 var elements = this.spawn();
10773 var col1 = this;
10774 var col2 = other;
10775
10776 var add = function add(col, other) {
10777 for (var i = 0; i < col.length; i++) {
10778 var ele = col[i];
10779 var id = ele._private.data.id;
10780 var inOther = other.hasElementWithId(id);
10781
10782 if (!inOther) {
10783 elements.push(ele);
10784 }
10785 }
10786 };
10787
10788 add(col1, col2);
10789 add(col2, col1);
10790 return elements;
10791 },
10792 diff: function diff(other) {
10793 var cy = this._private.cy;
10794
10795 if (string(other)) {
10796 other = cy.$(other);
10797 }
10798
10799 var left = this.spawn();
10800 var right = this.spawn();
10801 var both = this.spawn();
10802 var col1 = this;
10803 var col2 = other;
10804
10805 var add = function add(col, other, retEles) {
10806 for (var i = 0; i < col.length; i++) {
10807 var ele = col[i];
10808 var id = ele._private.data.id;
10809 var inOther = other.hasElementWithId(id);
10810
10811 if (inOther) {
10812 both.merge(ele);
10813 } else {
10814 retEles.push(ele);
10815 }
10816 }
10817 };
10818
10819 add(col1, col2, left);
10820 add(col2, col1, right);
10821 return {
10822 left: left,
10823 right: right,
10824 both: both
10825 };
10826 },
10827 add: function add(toAdd) {
10828 var cy = this._private.cy;
10829
10830 if (!toAdd) {
10831 return this;
10832 }
10833
10834 if (string(toAdd)) {
10835 var selector = toAdd;
10836 toAdd = cy.mutableElements().filter(selector);
10837 }
10838
10839 var elements = this.spawnSelf();
10840
10841 for (var i = 0; i < toAdd.length; i++) {
10842 var ele = toAdd[i];
10843 var add = !this.has(ele);
10844
10845 if (add) {
10846 elements.push(ele);
10847 }
10848 }
10849
10850 return elements;
10851 },
10852 // in place merge on calling collection
10853 merge: function merge(toAdd) {
10854 var _p = this._private;
10855 var cy = _p.cy;
10856
10857 if (!toAdd) {
10858 return this;
10859 }
10860
10861 if (toAdd && string(toAdd)) {
10862 var selector = toAdd;
10863 toAdd = cy.mutableElements().filter(selector);
10864 }
10865
10866 var map = _p.map;
10867
10868 for (var i = 0; i < toAdd.length; i++) {
10869 var toAddEle = toAdd[i];
10870 var id = toAddEle._private.data.id;
10871 var add = !map.has(id);
10872
10873 if (add) {
10874 var index = this.length++;
10875 this[index] = toAddEle;
10876 map.set(id, {
10877 ele: toAddEle,
10878 index: index
10879 });
10880 }
10881 }
10882
10883 return this; // chaining
10884 },
10885 unmergeAt: function unmergeAt(i) {
10886 var ele = this[i];
10887 var id = ele.id();
10888 var _p = this._private;
10889 var map = _p.map; // remove ele
10890
10891 this[i] = undefined;
10892 map["delete"](id);
10893 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
10894
10895 if (this.length > 1 && !unmergedLastEle) {
10896 var lastEleI = this.length - 1;
10897 var lastEle = this[lastEleI];
10898 var lastEleId = lastEle._private.data.id;
10899 this[lastEleI] = undefined;
10900 this[i] = lastEle;
10901 map.set(lastEleId, {
10902 ele: lastEle,
10903 index: i
10904 });
10905 } // the collection is now 1 ele smaller
10906
10907
10908 this.length--;
10909 return this;
10910 },
10911 // remove single ele in place in calling collection
10912 unmergeOne: function unmergeOne(ele) {
10913 ele = ele[0];
10914 var _p = this._private;
10915 var id = ele._private.data.id;
10916 var map = _p.map;
10917 var entry = map.get(id);
10918
10919 if (!entry) {
10920 return this; // no need to remove
10921 }
10922
10923 var i = entry.index;
10924 this.unmergeAt(i);
10925 return this;
10926 },
10927 // remove eles in place on calling collection
10928 unmerge: function unmerge(toRemove) {
10929 var cy = this._private.cy;
10930
10931 if (!toRemove) {
10932 return this;
10933 }
10934
10935 if (toRemove && string(toRemove)) {
10936 var selector = toRemove;
10937 toRemove = cy.mutableElements().filter(selector);
10938 }
10939
10940 for (var i = 0; i < toRemove.length; i++) {
10941 this.unmergeOne(toRemove[i]);
10942 }
10943
10944 return this; // chaining
10945 },
10946 unmergeBy: function unmergeBy(toRmFn) {
10947 for (var i = this.length - 1; i >= 0; i--) {
10948 var ele = this[i];
10949
10950 if (toRmFn(ele)) {
10951 this.unmergeAt(i);
10952 }
10953 }
10954
10955 return this;
10956 },
10957 map: function map(mapFn, thisArg) {
10958 var arr = [];
10959 var eles = this;
10960
10961 for (var i = 0; i < eles.length; i++) {
10962 var ele = eles[i];
10963 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
10964 arr.push(ret);
10965 }
10966
10967 return arr;
10968 },
10969 reduce: function reduce(fn, initialValue) {
10970 var val = initialValue;
10971 var eles = this;
10972
10973 for (var i = 0; i < eles.length; i++) {
10974 val = fn(val, eles[i], i, eles);
10975 }
10976
10977 return val;
10978 },
10979 max: function max(valFn, thisArg) {
10980 var max = -Infinity;
10981 var maxEle;
10982 var eles = this;
10983
10984 for (var i = 0; i < eles.length; i++) {
10985 var ele = eles[i];
10986 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
10987
10988 if (val > max) {
10989 max = val;
10990 maxEle = ele;
10991 }
10992 }
10993
10994 return {
10995 value: max,
10996 ele: maxEle
10997 };
10998 },
10999 min: function min(valFn, thisArg) {
11000 var min = Infinity;
11001 var minEle;
11002 var eles = this;
11003
11004 for (var i = 0; i < eles.length; i++) {
11005 var ele = eles[i];
11006 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11007
11008 if (val < min) {
11009 min = val;
11010 minEle = ele;
11011 }
11012 }
11013
11014 return {
11015 value: min,
11016 ele: minEle
11017 };
11018 }
11019}; // aliases
11020
11021var fn$5 = elesfn$n;
11022fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
11023fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
11024fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
11025fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
11026fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
11027fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
11028
11029var elesfn$o = {
11030 isNode: function isNode() {
11031 return this.group() === 'nodes';
11032 },
11033 isEdge: function isEdge() {
11034 return this.group() === 'edges';
11035 },
11036 isLoop: function isLoop() {
11037 return this.isEdge() && this.source()[0] === this.target()[0];
11038 },
11039 isSimple: function isSimple() {
11040 return this.isEdge() && this.source()[0] !== this.target()[0];
11041 },
11042 group: function group() {
11043 var ele = this[0];
11044
11045 if (ele) {
11046 return ele._private.group;
11047 }
11048 }
11049};
11050
11051/**
11052 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11053 * and z-index (low to high). These styles affect how this applies:
11054 *
11055 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11056 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11057 * root to leaves of the compound graph. The last drawn is `top`.
11058 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11059 * `manual` ignores this convention and draws based on the `z-index` value setting.
11060 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11061 * `z-index` will be drawn on top of an element with a lower `z-index`.
11062 */
11063
11064var zIndexSort = function zIndexSort(a, b) {
11065 var cy = a.cy();
11066 var hasCompoundNodes = cy.hasCompoundNodes();
11067
11068 function getDepth(ele) {
11069 var style = ele.pstyle('z-compound-depth');
11070
11071 if (style.value === 'auto') {
11072 return hasCompoundNodes ? ele.zDepth() : 0;
11073 } else if (style.value === 'bottom') {
11074 return -1;
11075 } else if (style.value === 'top') {
11076 return MAX_INT;
11077 } // 'orphan'
11078
11079
11080 return 0;
11081 }
11082
11083 var depthDiff = getDepth(a) - getDepth(b);
11084
11085 if (depthDiff !== 0) {
11086 return depthDiff;
11087 }
11088
11089 function getEleDepth(ele) {
11090 var style = ele.pstyle('z-index-compare');
11091
11092 if (style.value === 'auto') {
11093 return ele.isNode() ? 1 : 0;
11094 } // 'manual'
11095
11096
11097 return 0;
11098 }
11099
11100 var eleDiff = getEleDepth(a) - getEleDepth(b);
11101
11102 if (eleDiff !== 0) {
11103 return eleDiff;
11104 }
11105
11106 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11107
11108 if (zDiff !== 0) {
11109 return zDiff;
11110 } // compare indices in the core (order added to graph w/ last on top)
11111
11112
11113 return a.poolIndex() - b.poolIndex();
11114};
11115
11116var elesfn$p = {
11117 forEach: function forEach(fn$1, thisArg) {
11118 if (fn(fn$1)) {
11119 var N = this.length;
11120
11121 for (var i = 0; i < N; i++) {
11122 var ele = this[i];
11123 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
11124
11125 if (ret === false) {
11126 break;
11127 } // exit each early on return false
11128
11129 }
11130 }
11131
11132 return this;
11133 },
11134 toArray: function toArray() {
11135 var array = [];
11136
11137 for (var i = 0; i < this.length; i++) {
11138 array.push(this[i]);
11139 }
11140
11141 return array;
11142 },
11143 slice: function slice(start, end) {
11144 var array = [];
11145 var thisSize = this.length;
11146
11147 if (end == null) {
11148 end = thisSize;
11149 }
11150
11151 if (start == null) {
11152 start = 0;
11153 }
11154
11155 if (start < 0) {
11156 start = thisSize + start;
11157 }
11158
11159 if (end < 0) {
11160 end = thisSize + end;
11161 }
11162
11163 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11164 array.push(this[i]);
11165 }
11166
11167 return this.spawn(array);
11168 },
11169 size: function size() {
11170 return this.length;
11171 },
11172 eq: function eq(i) {
11173 return this[i] || this.spawn();
11174 },
11175 first: function first() {
11176 return this[0] || this.spawn();
11177 },
11178 last: function last() {
11179 return this[this.length - 1] || this.spawn();
11180 },
11181 empty: function empty() {
11182 return this.length === 0;
11183 },
11184 nonempty: function nonempty() {
11185 return !this.empty();
11186 },
11187 sort: function sort(sortFn) {
11188 if (!fn(sortFn)) {
11189 return this;
11190 }
11191
11192 var sorted = this.toArray().sort(sortFn);
11193 return this.spawn(sorted);
11194 },
11195 sortByZIndex: function sortByZIndex() {
11196 return this.sort(zIndexSort);
11197 },
11198 zDepth: function zDepth() {
11199 var ele = this[0];
11200
11201 if (!ele) {
11202 return undefined;
11203 } // let cy = ele.cy();
11204
11205
11206 var _p = ele._private;
11207 var group = _p.group;
11208
11209 if (group === 'nodes') {
11210 var depth = _p.data.parent ? ele.parents().size() : 0;
11211
11212 if (!ele.isParent()) {
11213 return MAX_INT - 1; // childless nodes always on top
11214 }
11215
11216 return depth;
11217 } else {
11218 var src = _p.source;
11219 var tgt = _p.target;
11220 var srcDepth = src.zDepth();
11221 var tgtDepth = tgt.zDepth();
11222 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11223 }
11224 }
11225};
11226elesfn$p.each = elesfn$p.forEach;
11227
11228var defineSymbolIterator = function defineSymbolIterator() {
11229 var typeofUndef = "undefined" ;
11230 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
11231
11232 if (isIteratorSupported) {
11233 elesfn$p[Symbol.iterator] = function () {
11234 var _this = this;
11235
11236 // eslint-disable-line no-undef
11237 var entry = {
11238 value: undefined,
11239 done: false
11240 };
11241 var i = 0;
11242 var length = this.length;
11243 return _defineProperty({
11244 next: function next() {
11245 if (i < length) {
11246 entry.value = _this[i++];
11247 } else {
11248 entry.value = undefined;
11249 entry.done = true;
11250 }
11251
11252 return entry;
11253 }
11254 }, Symbol.iterator, function () {
11255 // eslint-disable-line no-undef
11256 return this;
11257 });
11258 };
11259 }
11260};
11261
11262defineSymbolIterator();
11263
11264var getLayoutDimensionOptions = defaults({
11265 nodeDimensionsIncludeLabels: false
11266});
11267var elesfn$q = {
11268 // Calculates and returns node dimensions { x, y } based on options given
11269 layoutDimensions: function layoutDimensions(options) {
11270 options = getLayoutDimensionOptions(options);
11271 var dims;
11272
11273 if (!this.takesUpSpace()) {
11274 dims = {
11275 w: 0,
11276 h: 0
11277 };
11278 } else if (options.nodeDimensionsIncludeLabels) {
11279 var bbDim = this.boundingBox();
11280 dims = {
11281 w: bbDim.w,
11282 h: bbDim.h
11283 };
11284 } else {
11285 dims = {
11286 w: this.outerWidth(),
11287 h: this.outerHeight()
11288 };
11289 } // sanitise the dimensions for external layouts (avoid division by zero)
11290
11291
11292 if (dims.w === 0 || dims.h === 0) {
11293 dims.w = dims.h = 1;
11294 }
11295
11296 return dims;
11297 },
11298 // using standard layout options, apply position function (w/ or w/o animation)
11299 layoutPositions: function layoutPositions(layout, options, fn) {
11300 var nodes = this.nodes().filter(function (n) {
11301 return !n.isParent();
11302 });
11303 var cy = this.cy();
11304 var layoutEles = options.eles; // nodes & edges
11305
11306 var getMemoizeKey = function getMemoizeKey(node) {
11307 return node.id();
11308 };
11309
11310 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
11311
11312 layout.emit({
11313 type: 'layoutstart',
11314 layout: layout
11315 });
11316 layout.animations = [];
11317
11318 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
11319 var center = {
11320 x: nodesBb.x1 + nodesBb.w / 2,
11321 y: nodesBb.y1 + nodesBb.h / 2
11322 };
11323 var spacingVector = {
11324 // scale from center of bounding box (not necessarily 0,0)
11325 x: (pos.x - center.x) * spacing,
11326 y: (pos.y - center.y) * spacing
11327 };
11328 return {
11329 x: center.x + spacingVector.x,
11330 y: center.y + spacingVector.y
11331 };
11332 };
11333
11334 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
11335
11336 var spacingBb = function spacingBb() {
11337 if (!useSpacingFactor) {
11338 return null;
11339 }
11340
11341 var bb = makeBoundingBox();
11342
11343 for (var i = 0; i < nodes.length; i++) {
11344 var node = nodes[i];
11345 var pos = fnMem(node, i);
11346 expandBoundingBoxByPoint(bb, pos.x, pos.y);
11347 }
11348
11349 return bb;
11350 };
11351
11352 var bb = spacingBb();
11353 var getFinalPos = memoize(function (node, i) {
11354 var newPos = fnMem(node, i);
11355
11356 if (useSpacingFactor) {
11357 var spacing = Math.abs(options.spacingFactor);
11358 newPos = calculateSpacing(spacing, bb, newPos);
11359 }
11360
11361 if (options.transform != null) {
11362 newPos = options.transform(node, newPos);
11363 }
11364
11365 return newPos;
11366 }, getMemoizeKey);
11367
11368 if (options.animate) {
11369 for (var i = 0; i < nodes.length; i++) {
11370 var node = nodes[i];
11371 var newPos = getFinalPos(node, i);
11372 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
11373
11374 if (animateNode) {
11375 var ani = node.animation({
11376 position: newPos,
11377 duration: options.animationDuration,
11378 easing: options.animationEasing
11379 });
11380 layout.animations.push(ani);
11381 } else {
11382 node.position(newPos);
11383 }
11384 }
11385
11386 if (options.fit) {
11387 var fitAni = cy.animation({
11388 fit: {
11389 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
11390 padding: options.padding
11391 },
11392 duration: options.animationDuration,
11393 easing: options.animationEasing
11394 });
11395 layout.animations.push(fitAni);
11396 } else if (options.zoom !== undefined && options.pan !== undefined) {
11397 var zoomPanAni = cy.animation({
11398 zoom: options.zoom,
11399 pan: options.pan,
11400 duration: options.animationDuration,
11401 easing: options.animationEasing
11402 });
11403 layout.animations.push(zoomPanAni);
11404 }
11405
11406 layout.animations.forEach(function (ani) {
11407 return ani.play();
11408 });
11409 layout.one('layoutready', options.ready);
11410 layout.emit({
11411 type: 'layoutready',
11412 layout: layout
11413 });
11414 Promise$1.all(layout.animations.map(function (ani) {
11415 return ani.promise();
11416 })).then(function () {
11417 layout.one('layoutstop', options.stop);
11418 layout.emit({
11419 type: 'layoutstop',
11420 layout: layout
11421 });
11422 });
11423 } else {
11424 nodes.positions(getFinalPos);
11425
11426 if (options.fit) {
11427 cy.fit(options.eles, options.padding);
11428 }
11429
11430 if (options.zoom != null) {
11431 cy.zoom(options.zoom);
11432 }
11433
11434 if (options.pan) {
11435 cy.pan(options.pan);
11436 }
11437
11438 layout.one('layoutready', options.ready);
11439 layout.emit({
11440 type: 'layoutready',
11441 layout: layout
11442 });
11443 layout.one('layoutstop', options.stop);
11444 layout.emit({
11445 type: 'layoutstop',
11446 layout: layout
11447 });
11448 }
11449
11450 return this; // chaining
11451 },
11452 layout: function layout(options) {
11453 var cy = this.cy();
11454 return cy.makeLayout(extend({}, options, {
11455 eles: this
11456 }));
11457 }
11458}; // aliases:
11459
11460elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
11461
11462function styleCache(key, fn, ele) {
11463 var _p = ele._private;
11464 var cache = _p.styleCache = _p.styleCache || [];
11465 var val;
11466
11467 if ((val = cache[key]) != null) {
11468 return val;
11469 } else {
11470 val = cache[key] = fn(ele);
11471 return val;
11472 }
11473}
11474
11475function cacheStyleFunction(key, fn) {
11476 key = hashString(key);
11477 return function cachedStyleFunction(ele) {
11478 return styleCache(key, fn, ele);
11479 };
11480}
11481
11482function cachePrototypeStyleFunction(key, fn) {
11483 key = hashString(key);
11484
11485 var selfFn = function selfFn(ele) {
11486 return fn.call(ele);
11487 };
11488
11489 return function cachedPrototypeStyleFunction() {
11490 var ele = this[0];
11491
11492 if (ele) {
11493 return styleCache(key, selfFn, ele);
11494 }
11495 };
11496}
11497
11498var elesfn$r = {
11499 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
11500 var cy = this.cy();
11501 var renderer = cy.renderer();
11502 var styleEnabled = cy.styleEnabled();
11503
11504 if (renderer && styleEnabled) {
11505 renderer.recalculateRenderedStyle(this, useCache);
11506 }
11507
11508 return this;
11509 },
11510 dirtyStyleCache: function dirtyStyleCache() {
11511 var cy = this.cy();
11512
11513 var dirty = function dirty(ele) {
11514 return ele._private.styleCache = null;
11515 };
11516
11517 if (cy.hasCompoundNodes()) {
11518 var eles;
11519 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11520 eles.merge(eles.connectedEdges());
11521 eles.forEach(dirty);
11522 } else {
11523 this.forEach(function (ele) {
11524 dirty(ele);
11525 ele.connectedEdges().forEach(dirty);
11526 });
11527 }
11528
11529 return this;
11530 },
11531 // fully updates (recalculates) the style for the elements
11532 updateStyle: function updateStyle(notifyRenderer) {
11533 var cy = this._private.cy;
11534
11535 if (!cy.styleEnabled()) {
11536 return this;
11537 }
11538
11539 if (cy.batching()) {
11540 var bEles = cy._private.batchStyleEles;
11541 bEles.merge(this);
11542 return this; // chaining and exit early when batching
11543 }
11544
11545 var hasCompounds = cy.hasCompoundNodes();
11546 var updatedEles = this;
11547 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
11548
11549 if (hasCompounds) {
11550 // then add everything up and down for compound selector checks
11551 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11552 } // let changedEles = style.apply( updatedEles );
11553
11554
11555 var changedEles = updatedEles;
11556
11557 if (notifyRenderer) {
11558 changedEles.emitAndNotify('style'); // let renderer know we changed style
11559 } else {
11560 changedEles.emit('style'); // just fire the event
11561 }
11562
11563 updatedEles.forEach(function (ele) {
11564 return ele._private.styleDirty = true;
11565 });
11566 return this; // chaining
11567 },
11568 // private: clears dirty flag and recalculates style
11569 cleanStyle: function cleanStyle() {
11570 var cy = this.cy();
11571
11572 if (!cy.styleEnabled()) {
11573 return;
11574 }
11575
11576 for (var i = 0; i < this.length; i++) {
11577 var ele = this[i];
11578
11579 if (ele._private.styleDirty) {
11580 // n.b. this flag should be set before apply() to avoid potential infinite recursion
11581 ele._private.styleDirty = false;
11582 cy.style().apply(ele);
11583 }
11584 }
11585 },
11586 // get the internal parsed style object for the specified property
11587 parsedStyle: function parsedStyle(property) {
11588 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11589 var ele = this[0];
11590 var cy = ele.cy();
11591
11592 if (!cy.styleEnabled()) {
11593 return;
11594 }
11595
11596 if (ele) {
11597 this.cleanStyle();
11598 var overriddenStyle = ele._private.style[property];
11599
11600 if (overriddenStyle != null) {
11601 return overriddenStyle;
11602 } else if (includeNonDefault) {
11603 return cy.style().getDefaultProperty(property);
11604 } else {
11605 return null;
11606 }
11607 }
11608 },
11609 numericStyle: function numericStyle(property) {
11610 var ele = this[0];
11611
11612 if (!ele.cy().styleEnabled()) {
11613 return;
11614 }
11615
11616 if (ele) {
11617 var pstyle = ele.pstyle(property);
11618 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
11619 }
11620 },
11621 numericStyleUnits: function numericStyleUnits(property) {
11622 var ele = this[0];
11623
11624 if (!ele.cy().styleEnabled()) {
11625 return;
11626 }
11627
11628 if (ele) {
11629 return ele.pstyle(property).units;
11630 }
11631 },
11632 // get the specified css property as a rendered value (i.e. on-screen value)
11633 // or get the whole rendered style if no property specified (NB doesn't allow setting)
11634 renderedStyle: function renderedStyle(property) {
11635 var cy = this.cy();
11636
11637 if (!cy.styleEnabled()) {
11638 return this;
11639 }
11640
11641 var ele = this[0];
11642
11643 if (ele) {
11644 return cy.style().getRenderedStyle(ele, property);
11645 }
11646 },
11647 // read the calculated css style of the element or override the style (via a bypass)
11648 style: function style(name, value) {
11649 var cy = this.cy();
11650
11651 if (!cy.styleEnabled()) {
11652 return this;
11653 }
11654
11655 var updateTransitions = false;
11656 var style = cy.style();
11657
11658 if (plainObject(name)) {
11659 // then extend the bypass
11660 var props = name;
11661 style.applyBypass(this, props, updateTransitions);
11662 this.emitAndNotify('style'); // let the renderer know we've updated style
11663 } else if (string(name)) {
11664 if (value === undefined) {
11665 // then get the property from the style
11666 var ele = this[0];
11667
11668 if (ele) {
11669 return style.getStylePropertyValue(ele, name);
11670 } else {
11671 // empty collection => can't get any value
11672 return;
11673 }
11674 } else {
11675 // then set the bypass with the property value
11676 style.applyBypass(this, name, value, updateTransitions);
11677 this.emitAndNotify('style'); // let the renderer know we've updated style
11678 }
11679 } else if (name === undefined) {
11680 var _ele = this[0];
11681
11682 if (_ele) {
11683 return style.getRawStyle(_ele);
11684 } else {
11685 // empty collection => can't get any value
11686 return;
11687 }
11688 }
11689
11690 return this; // chaining
11691 },
11692 removeStyle: function removeStyle(names) {
11693 var cy = this.cy();
11694
11695 if (!cy.styleEnabled()) {
11696 return this;
11697 }
11698
11699 var updateTransitions = false;
11700 var style = cy.style();
11701 var eles = this;
11702
11703 if (names === undefined) {
11704 for (var i = 0; i < eles.length; i++) {
11705 var ele = eles[i];
11706 style.removeAllBypasses(ele, updateTransitions);
11707 }
11708 } else {
11709 names = names.split(/\s+/);
11710
11711 for (var _i = 0; _i < eles.length; _i++) {
11712 var _ele2 = eles[_i];
11713 style.removeBypasses(_ele2, names, updateTransitions);
11714 }
11715 }
11716
11717 this.emitAndNotify('style'); // let the renderer know we've updated style
11718
11719 return this; // chaining
11720 },
11721 show: function show() {
11722 this.css('display', 'element');
11723 return this; // chaining
11724 },
11725 hide: function hide() {
11726 this.css('display', 'none');
11727 return this; // chaining
11728 },
11729 effectiveOpacity: function effectiveOpacity() {
11730 var cy = this.cy();
11731
11732 if (!cy.styleEnabled()) {
11733 return 1;
11734 }
11735
11736 var hasCompoundNodes = cy.hasCompoundNodes();
11737 var ele = this[0];
11738
11739 if (ele) {
11740 var _p = ele._private;
11741 var parentOpacity = ele.pstyle('opacity').value;
11742
11743 if (!hasCompoundNodes) {
11744 return parentOpacity;
11745 }
11746
11747 var parents = !_p.data.parent ? null : ele.parents();
11748
11749 if (parents) {
11750 for (var i = 0; i < parents.length; i++) {
11751 var parent = parents[i];
11752 var opacity = parent.pstyle('opacity').value;
11753 parentOpacity = opacity * parentOpacity;
11754 }
11755 }
11756
11757 return parentOpacity;
11758 }
11759 },
11760 transparent: function transparent() {
11761 var cy = this.cy();
11762
11763 if (!cy.styleEnabled()) {
11764 return false;
11765 }
11766
11767 var ele = this[0];
11768 var hasCompoundNodes = ele.cy().hasCompoundNodes();
11769
11770 if (ele) {
11771 if (!hasCompoundNodes) {
11772 return ele.pstyle('opacity').value === 0;
11773 } else {
11774 return ele.effectiveOpacity() === 0;
11775 }
11776 }
11777 },
11778 backgrounding: function backgrounding() {
11779 var cy = this.cy();
11780
11781 if (!cy.styleEnabled()) {
11782 return false;
11783 }
11784
11785 var ele = this[0];
11786 return ele._private.backgrounding ? true : false;
11787 }
11788};
11789
11790function checkCompound(ele, parentOk) {
11791 var _p = ele._private;
11792 var parents = _p.data.parent ? ele.parents() : null;
11793
11794 if (parents) {
11795 for (var i = 0; i < parents.length; i++) {
11796 var parent = parents[i];
11797
11798 if (!parentOk(parent)) {
11799 return false;
11800 }
11801 }
11802 }
11803
11804 return true;
11805}
11806
11807function defineDerivedStateFunction(specs) {
11808 var ok = specs.ok;
11809 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
11810 var parentOk = specs.parentOk || specs.ok;
11811 return function () {
11812 var cy = this.cy();
11813
11814 if (!cy.styleEnabled()) {
11815 return true;
11816 }
11817
11818 var ele = this[0];
11819 var hasCompoundNodes = cy.hasCompoundNodes();
11820
11821 if (ele) {
11822 var _p = ele._private;
11823
11824 if (!ok(ele)) {
11825 return false;
11826 }
11827
11828 if (ele.isNode()) {
11829 return !hasCompoundNodes || checkCompound(ele, parentOk);
11830 } else {
11831 var src = _p.source;
11832 var tgt = _p.target;
11833 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
11834 }
11835 }
11836 };
11837}
11838
11839var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
11840 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
11841});
11842elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
11843 ok: eleTakesUpSpace
11844}));
11845var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
11846 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
11847});
11848var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
11849 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
11850});
11851elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
11852 ok: eleInteractive,
11853 parentOk: parentInteractive,
11854 edgeOkViaNode: eleTakesUpSpace
11855}));
11856
11857elesfn$r.noninteractive = function () {
11858 var ele = this[0];
11859
11860 if (ele) {
11861 return !ele.interactive();
11862 }
11863};
11864
11865var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
11866 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
11867});
11868var edgeVisibleViaNode = eleTakesUpSpace;
11869elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
11870 ok: eleVisible,
11871 edgeOkViaNode: edgeVisibleViaNode
11872}));
11873
11874elesfn$r.hidden = function () {
11875 var ele = this[0];
11876
11877 if (ele) {
11878 return !ele.visible();
11879 }
11880};
11881
11882elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
11883 if (!this.cy().styleEnabled()) {
11884 return false;
11885 }
11886
11887 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
11888});
11889elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
11890elesfn$r.renderedCss = elesfn$r.renderedStyle;
11891elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
11892elesfn$r.pstyle = elesfn$r.parsedStyle;
11893
11894var elesfn$s = {};
11895
11896function defineSwitchFunction(params) {
11897 return function () {
11898 var args = arguments;
11899 var changedEles = []; // e.g. cy.nodes().select( data, handler )
11900
11901 if (args.length === 2) {
11902 var data = args[0];
11903 var handler = args[1];
11904 this.on(params.event, data, handler);
11905 } // e.g. cy.nodes().select( handler )
11906 else if (args.length === 1 && fn(args[0])) {
11907 var _handler = args[0];
11908 this.on(params.event, _handler);
11909 } // e.g. cy.nodes().select()
11910 // e.g. (private) cy.nodes().select(['tapselect'])
11911 else if (args.length === 0 || args.length === 1 && array(args[0])) {
11912 var addlEvents = args.length === 1 ? args[0] : null;
11913
11914 for (var i = 0; i < this.length; i++) {
11915 var ele = this[i];
11916 var able = !params.ableField || ele._private[params.ableField];
11917 var changed = ele._private[params.field] != params.value;
11918
11919 if (params.overrideAble) {
11920 var overrideAble = params.overrideAble(ele);
11921
11922 if (overrideAble !== undefined) {
11923 able = overrideAble;
11924
11925 if (!overrideAble) {
11926 return this;
11927 } // to save cycles assume not able for all on override
11928
11929 }
11930 }
11931
11932 if (able) {
11933 ele._private[params.field] = params.value;
11934
11935 if (changed) {
11936 changedEles.push(ele);
11937 }
11938 }
11939 }
11940
11941 var changedColl = this.spawn(changedEles);
11942 changedColl.updateStyle(); // change of state => possible change of style
11943
11944 changedColl.emit(params.event);
11945
11946 if (addlEvents) {
11947 changedColl.emit(addlEvents);
11948 }
11949 }
11950
11951 return this;
11952 };
11953}
11954
11955function defineSwitchSet(params) {
11956 elesfn$s[params.field] = function () {
11957 var ele = this[0];
11958
11959 if (ele) {
11960 if (params.overrideField) {
11961 var val = params.overrideField(ele);
11962
11963 if (val !== undefined) {
11964 return val;
11965 }
11966 }
11967
11968 return ele._private[params.field];
11969 }
11970 };
11971
11972 elesfn$s[params.on] = defineSwitchFunction({
11973 event: params.on,
11974 field: params.field,
11975 ableField: params.ableField,
11976 overrideAble: params.overrideAble,
11977 value: true
11978 });
11979 elesfn$s[params.off] = defineSwitchFunction({
11980 event: params.off,
11981 field: params.field,
11982 ableField: params.ableField,
11983 overrideAble: params.overrideAble,
11984 value: false
11985 });
11986}
11987
11988defineSwitchSet({
11989 field: 'locked',
11990 overrideField: function overrideField(ele) {
11991 return ele.cy().autolock() ? true : undefined;
11992 },
11993 on: 'lock',
11994 off: 'unlock'
11995});
11996defineSwitchSet({
11997 field: 'grabbable',
11998 overrideField: function overrideField(ele) {
11999 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12000 },
12001 on: 'grabify',
12002 off: 'ungrabify'
12003});
12004defineSwitchSet({
12005 field: 'selected',
12006 ableField: 'selectable',
12007 overrideAble: function overrideAble(ele) {
12008 return ele.cy().autounselectify() ? false : undefined;
12009 },
12010 on: 'select',
12011 off: 'unselect'
12012});
12013defineSwitchSet({
12014 field: 'selectable',
12015 overrideField: function overrideField(ele) {
12016 return ele.cy().autounselectify() ? false : undefined;
12017 },
12018 on: 'selectify',
12019 off: 'unselectify'
12020});
12021elesfn$s.deselect = elesfn$s.unselect;
12022
12023elesfn$s.grabbed = function () {
12024 var ele = this[0];
12025
12026 if (ele) {
12027 return ele._private.grabbed;
12028 }
12029};
12030
12031defineSwitchSet({
12032 field: 'active',
12033 on: 'activate',
12034 off: 'unactivate'
12035});
12036defineSwitchSet({
12037 field: 'pannable',
12038 on: 'panify',
12039 off: 'unpanify'
12040});
12041
12042elesfn$s.inactive = function () {
12043 var ele = this[0];
12044
12045 if (ele) {
12046 return !ele._private.active;
12047 }
12048};
12049
12050var elesfn$t = {}; // DAG functions
12051////////////////
12052
12053var defineDagExtremity = function defineDagExtremity(params) {
12054 return function dagExtremityImpl(selector) {
12055 var eles = this;
12056 var ret = [];
12057
12058 for (var i = 0; i < eles.length; i++) {
12059 var ele = eles[i];
12060
12061 if (!ele.isNode()) {
12062 continue;
12063 }
12064
12065 var disqualified = false;
12066 var edges = ele.connectedEdges();
12067
12068 for (var j = 0; j < edges.length; j++) {
12069 var edge = edges[j];
12070 var src = edge.source();
12071 var tgt = edge.target();
12072
12073 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12074 disqualified = true;
12075 break;
12076 }
12077 }
12078
12079 if (!disqualified) {
12080 ret.push(ele);
12081 }
12082 }
12083
12084 return this.spawn(ret, true).filter(selector);
12085 };
12086};
12087
12088var defineDagOneHop = function defineDagOneHop(params) {
12089 return function (selector) {
12090 var eles = this;
12091 var oEles = [];
12092
12093 for (var i = 0; i < eles.length; i++) {
12094 var ele = eles[i];
12095
12096 if (!ele.isNode()) {
12097 continue;
12098 }
12099
12100 var edges = ele.connectedEdges();
12101
12102 for (var j = 0; j < edges.length; j++) {
12103 var edge = edges[j];
12104 var src = edge.source();
12105 var tgt = edge.target();
12106
12107 if (params.outgoing && src === ele) {
12108 oEles.push(edge);
12109 oEles.push(tgt);
12110 } else if (params.incoming && tgt === ele) {
12111 oEles.push(edge);
12112 oEles.push(src);
12113 }
12114 }
12115 }
12116
12117 return this.spawn(oEles, true).filter(selector);
12118 };
12119};
12120
12121var defineDagAllHops = function defineDagAllHops(params) {
12122 return function (selector) {
12123 var eles = this;
12124 var sEles = [];
12125 var sElesIds = {};
12126
12127 for (;;) {
12128 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12129
12130 if (next.length === 0) {
12131 break;
12132 } // done if none left
12133
12134
12135 var newNext = false;
12136
12137 for (var i = 0; i < next.length; i++) {
12138 var n = next[i];
12139 var nid = n.id();
12140
12141 if (!sElesIds[nid]) {
12142 sElesIds[nid] = true;
12143 sEles.push(n);
12144 newNext = true;
12145 }
12146 }
12147
12148 if (!newNext) {
12149 break;
12150 } // done if touched all outgoers already
12151
12152
12153 eles = next;
12154 }
12155
12156 return this.spawn(sEles, true).filter(selector);
12157 };
12158};
12159
12160elesfn$t.clearTraversalCache = function () {
12161 for (var i = 0; i < this.length; i++) {
12162 this[i]._private.traversalCache = null;
12163 }
12164};
12165
12166extend(elesfn$t, {
12167 // get the root nodes in the DAG
12168 roots: defineDagExtremity({
12169 noIncomingEdges: true
12170 }),
12171 // get the leaf nodes in the DAG
12172 leaves: defineDagExtremity({
12173 noOutgoingEdges: true
12174 }),
12175 // normally called children in graph theory
12176 // these nodes =edges=> outgoing nodes
12177 outgoers: cache(defineDagOneHop({
12178 outgoing: true
12179 }), 'outgoers'),
12180 // aka DAG descendants
12181 successors: defineDagAllHops({
12182 outgoing: true
12183 }),
12184 // normally called parents in graph theory
12185 // these nodes <=edges= incoming nodes
12186 incomers: cache(defineDagOneHop({
12187 incoming: true
12188 }), 'incomers'),
12189 // aka DAG ancestors
12190 predecessors: defineDagAllHops({
12191 incoming: true
12192 })
12193}); // Neighbourhood functions
12194//////////////////////////
12195
12196extend(elesfn$t, {
12197 neighborhood: cache(function (selector) {
12198 var elements = [];
12199 var nodes = this.nodes();
12200
12201 for (var i = 0; i < nodes.length; i++) {
12202 // for all nodes
12203 var node = nodes[i];
12204 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12205
12206 for (var j = 0; j < connectedEdges.length; j++) {
12207 var edge = connectedEdges[j];
12208 var src = edge.source();
12209 var tgt = edge.target();
12210 var otherNode = node === src ? tgt : src; // need check in case of loop
12211
12212 if (otherNode.length > 0) {
12213 elements.push(otherNode[0]); // add node 1 hop away
12214 } // add connected edge
12215
12216
12217 elements.push(edge[0]);
12218 }
12219 }
12220
12221 return this.spawn(elements, true).filter(selector);
12222 }, 'neighborhood'),
12223 closedNeighborhood: function closedNeighborhood(selector) {
12224 return this.neighborhood().add(this).filter(selector);
12225 },
12226 openNeighborhood: function openNeighborhood(selector) {
12227 return this.neighborhood(selector);
12228 }
12229}); // aliases
12230
12231elesfn$t.neighbourhood = elesfn$t.neighborhood;
12232elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
12233elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
12234/////////////////
12235
12236extend(elesfn$t, {
12237 source: cache(function sourceImpl(selector) {
12238 var ele = this[0];
12239 var src;
12240
12241 if (ele) {
12242 src = ele._private.source || ele.cy().collection();
12243 }
12244
12245 return src && selector ? src.filter(selector) : src;
12246 }, 'source'),
12247 target: cache(function targetImpl(selector) {
12248 var ele = this[0];
12249 var tgt;
12250
12251 if (ele) {
12252 tgt = ele._private.target || ele.cy().collection();
12253 }
12254
12255 return tgt && selector ? tgt.filter(selector) : tgt;
12256 }, 'target'),
12257 sources: defineSourceFunction({
12258 attr: 'source'
12259 }),
12260 targets: defineSourceFunction({
12261 attr: 'target'
12262 })
12263});
12264
12265function defineSourceFunction(params) {
12266 return function sourceImpl(selector) {
12267 var sources = [];
12268
12269 for (var i = 0; i < this.length; i++) {
12270 var ele = this[i];
12271 var src = ele._private[params.attr];
12272
12273 if (src) {
12274 sources.push(src);
12275 }
12276 }
12277
12278 return this.spawn(sources, true).filter(selector);
12279 };
12280}
12281
12282extend(elesfn$t, {
12283 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
12284 edgesTo: cache(defineEdgesWithFunction({
12285 thisIsSrc: true
12286 }), 'edgesTo')
12287});
12288
12289function defineEdgesWithFunction(params) {
12290 return function edgesWithImpl(otherNodes) {
12291 var elements = [];
12292 var cy = this._private.cy;
12293 var p = params || {}; // get elements if a selector is specified
12294
12295 if (string(otherNodes)) {
12296 otherNodes = cy.$(otherNodes);
12297 }
12298
12299 for (var h = 0; h < otherNodes.length; h++) {
12300 var edges = otherNodes[h]._private.edges;
12301
12302 for (var i = 0; i < edges.length; i++) {
12303 var edge = edges[i];
12304 var edgeData = edge._private.data;
12305 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
12306 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
12307 var edgeConnectsThisAndOther = thisToOther || otherToThis;
12308
12309 if (!edgeConnectsThisAndOther) {
12310 continue;
12311 }
12312
12313 if (p.thisIsSrc || p.thisIsTgt) {
12314 if (p.thisIsSrc && !thisToOther) {
12315 continue;
12316 }
12317
12318 if (p.thisIsTgt && !otherToThis) {
12319 continue;
12320 }
12321 }
12322
12323 elements.push(edge);
12324 }
12325 }
12326
12327 return this.spawn(elements, true);
12328 };
12329}
12330
12331extend(elesfn$t, {
12332 connectedEdges: cache(function (selector) {
12333 var retEles = [];
12334 var eles = this;
12335
12336 for (var i = 0; i < eles.length; i++) {
12337 var node = eles[i];
12338
12339 if (!node.isNode()) {
12340 continue;
12341 }
12342
12343 var edges = node._private.edges;
12344
12345 for (var j = 0; j < edges.length; j++) {
12346 var edge = edges[j];
12347 retEles.push(edge);
12348 }
12349 }
12350
12351 return this.spawn(retEles, true).filter(selector);
12352 }, 'connectedEdges'),
12353 connectedNodes: cache(function (selector) {
12354 var retEles = [];
12355 var eles = this;
12356
12357 for (var i = 0; i < eles.length; i++) {
12358 var edge = eles[i];
12359
12360 if (!edge.isEdge()) {
12361 continue;
12362 }
12363
12364 retEles.push(edge.source()[0]);
12365 retEles.push(edge.target()[0]);
12366 }
12367
12368 return this.spawn(retEles, true).filter(selector);
12369 }, 'connectedNodes'),
12370 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
12371 codirectedEdges: cache(defineParallelEdgesFunction({
12372 codirected: true
12373 }), 'codirectedEdges')
12374});
12375
12376function defineParallelEdgesFunction(params) {
12377 var defaults = {
12378 codirected: false
12379 };
12380 params = extend({}, defaults, params);
12381 return function parallelEdgesImpl(selector) {
12382 // micro-optimised for renderer
12383 var elements = [];
12384 var edges = this.edges();
12385 var p = params; // look at all the edges in the collection
12386
12387 for (var i = 0; i < edges.length; i++) {
12388 var edge1 = edges[i];
12389 var edge1_p = edge1._private;
12390 var src1 = edge1_p.source;
12391 var srcid1 = src1._private.data.id;
12392 var tgtid1 = edge1_p.data.target;
12393 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
12394
12395 for (var j = 0; j < srcEdges1.length; j++) {
12396 var edge2 = srcEdges1[j];
12397 var edge2data = edge2._private.data;
12398 var tgtid2 = edge2data.target;
12399 var srcid2 = edge2data.source;
12400 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
12401 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
12402
12403 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
12404 elements.push(edge2);
12405 }
12406 }
12407 }
12408
12409 return this.spawn(elements, true).filter(selector);
12410 };
12411} // Misc functions
12412/////////////////
12413
12414
12415extend(elesfn$t, {
12416 components: function components(root) {
12417 var self = this;
12418 var cy = self.cy();
12419 var visited = cy.collection();
12420 var unvisited = root == null ? self.nodes() : root.nodes();
12421 var components = [];
12422
12423 if (root != null && unvisited.empty()) {
12424 // root may contain only edges
12425 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
12426 }
12427
12428 var visitInComponent = function visitInComponent(node, component) {
12429 visited.merge(node);
12430 unvisited.unmerge(node);
12431 component.merge(node);
12432 };
12433
12434 if (unvisited.empty()) {
12435 return self.spawn();
12436 }
12437
12438 var _loop = function _loop() {
12439 // each iteration yields a component
12440 var cmpt = cy.collection();
12441 components.push(cmpt);
12442 var root = unvisited[0];
12443 visitInComponent(root, cmpt);
12444 self.bfs({
12445 directed: false,
12446 roots: root,
12447 visit: function visit(v) {
12448 return visitInComponent(v, cmpt);
12449 }
12450 });
12451 cmpt.forEach(function (node) {
12452 node.connectedEdges().forEach(function (e) {
12453 // connectedEdges() usually cached
12454 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
12455 // has() is cheap
12456 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
12457 }
12458 });
12459 });
12460 };
12461
12462 do {
12463 _loop();
12464 } while (unvisited.length > 0);
12465
12466 return components;
12467 },
12468 component: function component() {
12469 var ele = this[0];
12470 return ele.cy().mutableElements().components(ele)[0];
12471 }
12472});
12473elesfn$t.componentsOf = elesfn$t.components;
12474
12475var Collection = function Collection(cy, elements) {
12476 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
12477
12478 if (cy === undefined) {
12479 error('A collection must have a reference to the core');
12480 return;
12481 }
12482
12483 var map = new Map$1();
12484 var createdElements = false;
12485
12486 if (!elements) {
12487 elements = [];
12488 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
12489 createdElements = true; // make elements from json and restore all at once later
12490
12491 var eles = [];
12492 var elesIds = new Set$1();
12493
12494 for (var i = 0, l = elements.length; i < l; i++) {
12495 var json = elements[i];
12496
12497 if (json.data == null) {
12498 json.data = {};
12499 }
12500
12501 var _data = json.data; // make sure newly created elements have valid ids
12502
12503 if (_data.id == null) {
12504 _data.id = uuid();
12505 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
12506 continue; // can't create element if prior id already exists
12507 }
12508
12509 var ele = new Element(cy, json, false);
12510 eles.push(ele);
12511 elesIds.add(_data.id);
12512 }
12513
12514 elements = eles;
12515 }
12516
12517 this.length = 0;
12518
12519 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
12520 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
12521
12522 if (element$1 == null) {
12523 continue;
12524 }
12525
12526 var id = element$1._private.data.id;
12527
12528 if (!unique || !map.has(id)) {
12529 if (unique) {
12530 map.set(id, {
12531 index: this.length,
12532 ele: element$1
12533 });
12534 }
12535
12536 this[this.length] = element$1;
12537 this.length++;
12538 }
12539 }
12540
12541 this._private = {
12542 eles: this,
12543 cy: cy,
12544
12545 get map() {
12546 if (this.lazyMap == null) {
12547 this.rebuildMap();
12548 }
12549
12550 return this.lazyMap;
12551 },
12552
12553 set map(m) {
12554 this.lazyMap = m;
12555 },
12556
12557 rebuildMap: function rebuildMap() {
12558 var m = this.lazyMap = new Map$1();
12559 var eles = this.eles;
12560
12561 for (var _i2 = 0; _i2 < eles.length; _i2++) {
12562 var _ele = eles[_i2];
12563 m.set(_ele.id(), {
12564 index: _i2,
12565 ele: _ele
12566 });
12567 }
12568 }
12569 };
12570
12571 if (unique) {
12572 this._private.map = map;
12573 } // restore the elements if we created them from json
12574
12575
12576 if (createdElements) {
12577 this.restore();
12578 }
12579}; // Functions
12580////////////////////////////////////////////////////////////////////////////////////////////////////
12581// keep the prototypes in sync (an element has the same functions as a collection)
12582// and use elefn and elesfn as shorthands to the prototypes
12583
12584
12585var elesfn$u = Element.prototype = Collection.prototype = Object.create(Array.prototype);
12586
12587elesfn$u.instanceString = function () {
12588 return 'collection';
12589};
12590
12591elesfn$u.spawn = function (eles, unique) {
12592 return new Collection(this.cy(), eles, unique);
12593};
12594
12595elesfn$u.spawnSelf = function () {
12596 return this.spawn(this);
12597};
12598
12599elesfn$u.cy = function () {
12600 return this._private.cy;
12601};
12602
12603elesfn$u.renderer = function () {
12604 return this._private.cy.renderer();
12605};
12606
12607elesfn$u.element = function () {
12608 return this[0];
12609};
12610
12611elesfn$u.collection = function () {
12612 if (collection(this)) {
12613 return this;
12614 } else {
12615 // an element
12616 return new Collection(this._private.cy, [this]);
12617 }
12618};
12619
12620elesfn$u.unique = function () {
12621 return new Collection(this._private.cy, this, true);
12622};
12623
12624elesfn$u.hasElementWithId = function (id) {
12625 id = '' + id; // id must be string
12626
12627 return this._private.map.has(id);
12628};
12629
12630elesfn$u.getElementById = function (id) {
12631 id = '' + id; // id must be string
12632
12633 var cy = this._private.cy;
12634
12635 var entry = this._private.map.get(id);
12636
12637 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
12638};
12639
12640elesfn$u.$id = elesfn$u.getElementById;
12641
12642elesfn$u.poolIndex = function () {
12643 var cy = this._private.cy;
12644 var eles = cy._private.elements;
12645 var id = this[0]._private.data.id;
12646 return eles._private.map.get(id).index;
12647};
12648
12649elesfn$u.indexOf = function (ele) {
12650 var id = ele[0]._private.data.id;
12651 return this._private.map.get(id).index;
12652};
12653
12654elesfn$u.indexOfId = function (id) {
12655 id = '' + id; // id must be string
12656
12657 return this._private.map.get(id).index;
12658};
12659
12660elesfn$u.json = function (obj) {
12661 var ele = this.element();
12662 var cy = this.cy();
12663
12664 if (ele == null && obj) {
12665 return this;
12666 } // can't set to no eles
12667
12668
12669 if (ele == null) {
12670 return undefined;
12671 } // can't get from no eles
12672
12673
12674 var p = ele._private;
12675
12676 if (plainObject(obj)) {
12677 // set
12678 cy.startBatch();
12679
12680 if (obj.data) {
12681 ele.data(obj.data);
12682 var _data2 = p.data;
12683
12684 if (ele.isEdge()) {
12685 // source and target are immutable via data()
12686 var move = false;
12687 var spec = {};
12688 var src = obj.data.source;
12689 var tgt = obj.data.target;
12690
12691 if (src != null && src != _data2.source) {
12692 spec.source = '' + src; // id must be string
12693
12694 move = true;
12695 }
12696
12697 if (tgt != null && tgt != _data2.target) {
12698 spec.target = '' + tgt; // id must be string
12699
12700 move = true;
12701 }
12702
12703 if (move) {
12704 ele = ele.move(spec);
12705 }
12706 } else {
12707 // parent is immutable via data()
12708 var newParentValSpecd = 'parent' in obj.data;
12709 var parent = obj.data.parent;
12710
12711 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
12712 if (parent === undefined) {
12713 // can't set undefined imperatively, so use null
12714 parent = null;
12715 }
12716
12717 if (parent != null) {
12718 parent = '' + parent; // id must be string
12719 }
12720
12721 ele = ele.move({
12722 parent: parent
12723 });
12724 }
12725 }
12726 }
12727
12728 if (obj.position) {
12729 ele.position(obj.position);
12730 } // ignore group -- immutable
12731
12732
12733 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
12734 var obj_k = obj[k];
12735
12736 if (obj_k != null && obj_k !== p[k]) {
12737 if (obj_k) {
12738 ele[trueFnName]();
12739 } else {
12740 ele[falseFnName]();
12741 }
12742 }
12743 };
12744
12745 checkSwitch('removed', 'remove', 'restore');
12746 checkSwitch('selected', 'select', 'unselect');
12747 checkSwitch('selectable', 'selectify', 'unselectify');
12748 checkSwitch('locked', 'lock', 'unlock');
12749 checkSwitch('grabbable', 'grabify', 'ungrabify');
12750 checkSwitch('pannable', 'panify', 'unpanify');
12751
12752 if (obj.classes != null) {
12753 ele.classes(obj.classes);
12754 }
12755
12756 cy.endBatch();
12757 return this;
12758 } else if (obj === undefined) {
12759 // get
12760 var json = {
12761 data: copy(p.data),
12762 position: copy(p.position),
12763 group: p.group,
12764 removed: p.removed,
12765 selected: p.selected,
12766 selectable: p.selectable,
12767 locked: p.locked,
12768 grabbable: p.grabbable,
12769 pannable: p.pannable,
12770 classes: null
12771 };
12772 json.classes = '';
12773 var i = 0;
12774 p.classes.forEach(function (cls) {
12775 return json.classes += i++ === 0 ? cls : ' ' + cls;
12776 });
12777 return json;
12778 }
12779};
12780
12781elesfn$u.jsons = function () {
12782 var jsons = [];
12783
12784 for (var i = 0; i < this.length; i++) {
12785 var ele = this[i];
12786 var json = ele.json();
12787 jsons.push(json);
12788 }
12789
12790 return jsons;
12791};
12792
12793elesfn$u.clone = function () {
12794 var cy = this.cy();
12795 var elesArr = [];
12796
12797 for (var i = 0; i < this.length; i++) {
12798 var ele = this[i];
12799 var json = ele.json();
12800 var clone = new Element(cy, json, false); // NB no restore
12801
12802 elesArr.push(clone);
12803 }
12804
12805 return new Collection(cy, elesArr);
12806};
12807
12808elesfn$u.copy = elesfn$u.clone;
12809
12810elesfn$u.restore = function () {
12811 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12812 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12813 var self = this;
12814 var cy = self.cy();
12815 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
12816 // restore the nodes first
12817
12818 var nodes = [];
12819 var edges = [];
12820 var elements;
12821
12822 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
12823 var ele = self[_i3];
12824
12825 if (addToPool && !ele.removed()) {
12826 // don't need to handle this ele
12827 continue;
12828 } // keep nodes first in the array and edges after
12829
12830
12831 if (ele.isNode()) {
12832 // put to front of array if node
12833 nodes.push(ele);
12834 } else {
12835 // put to end of array if edge
12836 edges.push(ele);
12837 }
12838 }
12839
12840 elements = nodes.concat(edges);
12841 var i;
12842
12843 var removeFromElements = function removeFromElements() {
12844 elements.splice(i, 1);
12845 i--;
12846 }; // now, restore each element
12847
12848
12849 for (i = 0; i < elements.length; i++) {
12850 var _ele2 = elements[i];
12851 var _private = _ele2._private;
12852 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
12853
12854 _ele2.clearTraversalCache(); // set id and validate
12855
12856
12857 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
12858 _data3.id = uuid();
12859 } else if (number(_data3.id)) {
12860 _data3.id = '' + _data3.id; // now it's a string
12861 } else if (emptyString(_data3.id) || !string(_data3.id)) {
12862 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
12863
12864 removeFromElements();
12865 continue;
12866 } else if (cy.hasElementWithId(_data3.id)) {
12867 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
12868
12869 removeFromElements();
12870 continue;
12871 }
12872
12873 var id = _data3.id; // id is finalised, now let's keep a ref
12874
12875 if (_ele2.isNode()) {
12876 // extra checks for nodes
12877 var pos = _private.position; // make sure the nodes have a defined position
12878
12879 if (pos.x == null) {
12880 pos.x = 0;
12881 }
12882
12883 if (pos.y == null) {
12884 pos.y = 0;
12885 }
12886 }
12887
12888 if (_ele2.isEdge()) {
12889 // extra checks for edges
12890 var edge = _ele2;
12891 var fields = ['source', 'target'];
12892 var fieldsLength = fields.length;
12893 var badSourceOrTarget = false;
12894
12895 for (var j = 0; j < fieldsLength; j++) {
12896 var field = fields[j];
12897 var val = _data3[field];
12898
12899 if (number(val)) {
12900 val = _data3[field] = '' + _data3[field]; // now string
12901 }
12902
12903 if (val == null || val === '') {
12904 // can't create if source or target is not defined properly
12905 error('Can not create edge `' + id + '` with unspecified ' + field);
12906 badSourceOrTarget = true;
12907 } else if (!cy.hasElementWithId(val)) {
12908 // can't create edge if one of its nodes doesn't exist
12909 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
12910 badSourceOrTarget = true;
12911 }
12912 }
12913
12914 if (badSourceOrTarget) {
12915 removeFromElements();
12916 continue;
12917 } // can't create this
12918
12919
12920 var src = cy.getElementById(_data3.source);
12921 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
12922
12923 if (src.same(tgt)) {
12924 src._private.edges.push(edge);
12925 } else {
12926 src._private.edges.push(edge);
12927
12928 tgt._private.edges.push(edge);
12929 }
12930
12931 edge._private.source = src;
12932 edge._private.target = tgt;
12933 } // if is edge
12934 // create mock ids / indexes maps for element so it can be used like collections
12935
12936
12937 _private.map = new Map$1();
12938
12939 _private.map.set(id, {
12940 ele: _ele2,
12941 index: 0
12942 });
12943
12944 _private.removed = false;
12945
12946 if (addToPool) {
12947 cy.addToPool(_ele2);
12948 }
12949 } // for each element
12950 // do compound node sanity checks
12951
12952
12953 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
12954 // each node
12955 var node = nodes[_i4];
12956 var _data4 = node._private.data;
12957
12958 if (number(_data4.parent)) {
12959 // then automake string
12960 _data4.parent = '' + _data4.parent;
12961 }
12962
12963 var parentId = _data4.parent;
12964 var specifiedParent = parentId != null;
12965
12966 if (specifiedParent) {
12967 var parent = cy.getElementById(parentId);
12968
12969 if (parent.empty()) {
12970 // non-existant parent; just remove it
12971 _data4.parent = undefined;
12972 } else {
12973 var selfAsParent = false;
12974 var ancestor = parent;
12975
12976 while (!ancestor.empty()) {
12977 if (node.same(ancestor)) {
12978 // mark self as parent and remove from data
12979 selfAsParent = true;
12980 _data4.parent = undefined; // remove parent reference
12981 // exit or we loop forever
12982
12983 break;
12984 }
12985
12986 ancestor = ancestor.parent();
12987 }
12988
12989 if (!selfAsParent) {
12990 // connect with children
12991 parent[0]._private.children.push(node);
12992
12993 node._private.parent = parent[0]; // let the core know we have a compound graph
12994
12995 cy_p.hasCompoundNodes = true;
12996 }
12997 } // else
12998
12999 } // if specified parent
13000
13001 } // for each node
13002
13003
13004 if (elements.length > 0) {
13005 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13006
13007 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13008 var _ele3 = restored[_i5];
13009
13010 if (_ele3.isNode()) {
13011 continue;
13012 } // adding an edge invalidates the traversal caches for the parallel edges
13013
13014
13015 _ele3.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13016
13017
13018 _ele3.source().clearTraversalCache();
13019
13020 _ele3.target().clearTraversalCache();
13021 }
13022
13023 var toUpdateStyle;
13024
13025 if (cy_p.hasCompoundNodes) {
13026 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13027 } else {
13028 toUpdateStyle = restored;
13029 }
13030
13031 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13032
13033 if (notifyRenderer) {
13034 restored.emitAndNotify('add');
13035 } else if (addToPool) {
13036 restored.emit('add');
13037 }
13038 }
13039
13040 return self; // chainability
13041};
13042
13043elesfn$u.removed = function () {
13044 var ele = this[0];
13045 return ele && ele._private.removed;
13046};
13047
13048elesfn$u.inside = function () {
13049 var ele = this[0];
13050 return ele && !ele._private.removed;
13051};
13052
13053elesfn$u.remove = function () {
13054 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13055 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13056 var self = this;
13057 var elesToRemove = [];
13058 var elesToRemoveIds = {};
13059 var cy = self._private.cy; // add connected edges
13060
13061 function addConnectedEdges(node) {
13062 var edges = node._private.edges;
13063
13064 for (var i = 0; i < edges.length; i++) {
13065 add(edges[i]);
13066 }
13067 } // add descendant nodes
13068
13069
13070 function addChildren(node) {
13071 var children = node._private.children;
13072
13073 for (var i = 0; i < children.length; i++) {
13074 add(children[i]);
13075 }
13076 }
13077
13078 function add(ele) {
13079 var alreadyAdded = elesToRemoveIds[ele.id()];
13080
13081 if (removeFromPool && ele.removed() || alreadyAdded) {
13082 return;
13083 } else {
13084 elesToRemoveIds[ele.id()] = true;
13085 }
13086
13087 if (ele.isNode()) {
13088 elesToRemove.push(ele); // nodes are removed last
13089
13090 addConnectedEdges(ele);
13091 addChildren(ele);
13092 } else {
13093 elesToRemove.unshift(ele); // edges are removed first
13094 }
13095 } // make the list of elements to remove
13096 // (may be removing more than specified due to connected edges etc)
13097
13098
13099 for (var i = 0, l = self.length; i < l; i++) {
13100 var ele = self[i];
13101 add(ele);
13102 }
13103
13104 function removeEdgeRef(node, edge) {
13105 var connectedEdges = node._private.edges;
13106 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13107
13108 node.clearTraversalCache();
13109 }
13110
13111 function removeParallelRef(pllEdge) {
13112 // removing an edge invalidates the traversal caches for the parallel edges
13113 pllEdge.clearTraversalCache();
13114 }
13115
13116 var alteredParents = [];
13117 alteredParents.ids = {};
13118
13119 function removeChildRef(parent, ele) {
13120 ele = ele[0];
13121 parent = parent[0];
13122 var children = parent._private.children;
13123 var pid = parent.id();
13124 removeFromArray(children, ele); // remove parent => child ref
13125
13126 ele._private.parent = null; // remove child => parent ref
13127
13128 if (!alteredParents.ids[pid]) {
13129 alteredParents.ids[pid] = true;
13130 alteredParents.push(parent);
13131 }
13132 }
13133
13134 self.dirtyCompoundBoundsCache();
13135
13136 if (removeFromPool) {
13137 cy.removeFromPool(elesToRemove); // remove from core pool
13138 }
13139
13140 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13141 var _ele4 = elesToRemove[_i6];
13142
13143 if (_ele4.isEdge()) {
13144 // remove references to this edge in its connected nodes
13145 var src = _ele4.source()[0];
13146
13147 var tgt = _ele4.target()[0];
13148
13149 removeEdgeRef(src, _ele4);
13150 removeEdgeRef(tgt, _ele4);
13151
13152 var pllEdges = _ele4.parallelEdges();
13153
13154 for (var j = 0; j < pllEdges.length; j++) {
13155 var pllEdge = pllEdges[j];
13156 removeParallelRef(pllEdge);
13157
13158 if (pllEdge.isBundledBezier()) {
13159 pllEdge.dirtyBoundingBoxCache();
13160 }
13161 }
13162 } else {
13163 // remove reference to parent
13164 var parent = _ele4.parent();
13165
13166 if (parent.length !== 0) {
13167 removeChildRef(parent, _ele4);
13168 }
13169 }
13170
13171 if (removeFromPool) {
13172 // mark as removed
13173 _ele4._private.removed = true;
13174 }
13175 } // check to see if we have a compound graph or not
13176
13177
13178 var elesStillInside = cy._private.elements;
13179 cy._private.hasCompoundNodes = false;
13180
13181 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13182 var _ele5 = elesStillInside[_i7];
13183
13184 if (_ele5.isParent()) {
13185 cy._private.hasCompoundNodes = true;
13186 break;
13187 }
13188 }
13189
13190 var removedElements = new Collection(this.cy(), elesToRemove);
13191
13192 if (removedElements.size() > 0) {
13193 // must manually notify since trigger won't do this automatically once removed
13194 if (notifyRenderer) {
13195 removedElements.emitAndNotify('remove');
13196 } else if (removeFromPool) {
13197 removedElements.emit('remove');
13198 }
13199 } // the parents who were modified by the removal need their style updated
13200
13201
13202 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13203 var _ele6 = alteredParents[_i8];
13204
13205 if (!removeFromPool || !_ele6.removed()) {
13206 _ele6.updateStyle();
13207 }
13208 }
13209
13210 return removedElements;
13211};
13212
13213elesfn$u.move = function (struct) {
13214 var cy = this._private.cy;
13215 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
13216 // (our calls to remove/restore do not remove from the graph or make events)
13217
13218 var notifyRenderer = false;
13219 var modifyPool = false;
13220
13221 var toString = function toString(id) {
13222 return id == null ? id : '' + id;
13223 }; // id must be string
13224
13225
13226 if (struct.source !== undefined || struct.target !== undefined) {
13227 var srcId = toString(struct.source);
13228 var tgtId = toString(struct.target);
13229 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13230 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13231
13232 if (srcExists || tgtExists) {
13233 cy.batch(function () {
13234 // avoid duplicate style updates
13235 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13236
13237 eles.emitAndNotify('moveout');
13238
13239 for (var i = 0; i < eles.length; i++) {
13240 var ele = eles[i];
13241 var _data5 = ele._private.data;
13242
13243 if (ele.isEdge()) {
13244 if (srcExists) {
13245 _data5.source = srcId;
13246 }
13247
13248 if (tgtExists) {
13249 _data5.target = tgtId;
13250 }
13251 }
13252 }
13253
13254 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13255 });
13256 eles.emitAndNotify('move');
13257 }
13258 } else if (struct.parent !== undefined) {
13259 // move node to new parent
13260 var parentId = toString(struct.parent);
13261 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13262
13263 if (parentExists) {
13264 var pidToAssign = parentId === null ? undefined : parentId;
13265 cy.batch(function () {
13266 // avoid duplicate style updates
13267 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13268
13269 updated.emitAndNotify('moveout');
13270
13271 for (var i = 0; i < eles.length; i++) {
13272 var ele = eles[i];
13273 var _data6 = ele._private.data;
13274
13275 if (ele.isNode()) {
13276 _data6.parent = pidToAssign;
13277 }
13278 }
13279
13280 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13281 });
13282 eles.emitAndNotify('move');
13283 }
13284 }
13285
13286 return this;
13287};
13288
13289[elesfn$c, elesfn$d, elesfn$e, elesfn$f, elesfn$g, data$1, elesfn$i, dimensions, elesfn$m, elesfn$n, elesfn$o, elesfn$p, elesfn$q, elesfn$r, elesfn$s, elesfn$t].forEach(function (props) {
13290 extend(elesfn$u, props);
13291});
13292
13293var corefn = {
13294 add: function add(opts) {
13295 var elements;
13296 var cy = this; // add the elements
13297
13298 if (elementOrCollection(opts)) {
13299 var eles = opts;
13300
13301 if (eles._private.cy === cy) {
13302 // same instance => just restore
13303 elements = eles.restore();
13304 } else {
13305 // otherwise, copy from json
13306 var jsons = [];
13307
13308 for (var i = 0; i < eles.length; i++) {
13309 var ele = eles[i];
13310 jsons.push(ele.json());
13311 }
13312
13313 elements = new Collection(cy, jsons);
13314 }
13315 } // specify an array of options
13316 else if (array(opts)) {
13317 var _jsons = opts;
13318 elements = new Collection(cy, _jsons);
13319 } // specify via opts.nodes and opts.edges
13320 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13321 var elesByGroup = opts;
13322 var _jsons2 = [];
13323 var grs = ['nodes', 'edges'];
13324
13325 for (var _i = 0, il = grs.length; _i < il; _i++) {
13326 var group = grs[_i];
13327 var elesArray = elesByGroup[group];
13328
13329 if (array(elesArray)) {
13330 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13331 var json = extend({
13332 group: group
13333 }, elesArray[j]);
13334
13335 _jsons2.push(json);
13336 }
13337 }
13338 }
13339
13340 elements = new Collection(cy, _jsons2);
13341 } // specify options for one element
13342 else {
13343 var _json = opts;
13344 elements = new Element(cy, _json).collection();
13345 }
13346
13347 return elements;
13348 },
13349 remove: function remove(collection) {
13350 if (elementOrCollection(collection)) ; else if (string(collection)) {
13351 var selector = collection;
13352 collection = this.$(selector);
13353 }
13354
13355 return collection.remove();
13356 }
13357};
13358
13359/* global Float32Array */
13360
13361/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13362function generateCubicBezier(mX1, mY1, mX2, mY2) {
13363 var NEWTON_ITERATIONS = 4,
13364 NEWTON_MIN_SLOPE = 0.001,
13365 SUBDIVISION_PRECISION = 0.0000001,
13366 SUBDIVISION_MAX_ITERATIONS = 10,
13367 kSplineTableSize = 11,
13368 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
13369 float32ArraySupported = typeof Float32Array !== 'undefined';
13370 /* Must contain four arguments. */
13371
13372 if (arguments.length !== 4) {
13373 return false;
13374 }
13375 /* Arguments must be numbers. */
13376
13377
13378 for (var i = 0; i < 4; ++i) {
13379 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
13380 return false;
13381 }
13382 }
13383 /* X values must be in the [0, 1] range. */
13384
13385
13386 mX1 = Math.min(mX1, 1);
13387 mX2 = Math.min(mX2, 1);
13388 mX1 = Math.max(mX1, 0);
13389 mX2 = Math.max(mX2, 0);
13390 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
13391
13392 function A(aA1, aA2) {
13393 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
13394 }
13395
13396 function B(aA1, aA2) {
13397 return 3.0 * aA2 - 6.0 * aA1;
13398 }
13399
13400 function C(aA1) {
13401 return 3.0 * aA1;
13402 }
13403
13404 function calcBezier(aT, aA1, aA2) {
13405 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
13406 }
13407
13408 function getSlope(aT, aA1, aA2) {
13409 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
13410 }
13411
13412 function newtonRaphsonIterate(aX, aGuessT) {
13413 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
13414 var currentSlope = getSlope(aGuessT, mX1, mX2);
13415
13416 if (currentSlope === 0.0) {
13417 return aGuessT;
13418 }
13419
13420 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
13421 aGuessT -= currentX / currentSlope;
13422 }
13423
13424 return aGuessT;
13425 }
13426
13427 function calcSampleValues() {
13428 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
13429 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
13430 }
13431 }
13432
13433 function binarySubdivide(aX, aA, aB) {
13434 var currentX,
13435 currentT,
13436 i = 0;
13437
13438 do {
13439 currentT = aA + (aB - aA) / 2.0;
13440 currentX = calcBezier(currentT, mX1, mX2) - aX;
13441
13442 if (currentX > 0.0) {
13443 aB = currentT;
13444 } else {
13445 aA = currentT;
13446 }
13447 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
13448
13449 return currentT;
13450 }
13451
13452 function getTForX(aX) {
13453 var intervalStart = 0.0,
13454 currentSample = 1,
13455 lastSample = kSplineTableSize - 1;
13456
13457 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
13458 intervalStart += kSampleStepSize;
13459 }
13460
13461 --currentSample;
13462 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
13463 guessForT = intervalStart + dist * kSampleStepSize,
13464 initialSlope = getSlope(guessForT, mX1, mX2);
13465
13466 if (initialSlope >= NEWTON_MIN_SLOPE) {
13467 return newtonRaphsonIterate(aX, guessForT);
13468 } else if (initialSlope === 0.0) {
13469 return guessForT;
13470 } else {
13471 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
13472 }
13473 }
13474
13475 var _precomputed = false;
13476
13477 function precompute() {
13478 _precomputed = true;
13479
13480 if (mX1 !== mY1 || mX2 !== mY2) {
13481 calcSampleValues();
13482 }
13483 }
13484
13485 var f = function f(aX) {
13486 if (!_precomputed) {
13487 precompute();
13488 }
13489
13490 if (mX1 === mY1 && mX2 === mY2) {
13491 return aX;
13492 }
13493
13494 if (aX === 0) {
13495 return 0;
13496 }
13497
13498 if (aX === 1) {
13499 return 1;
13500 }
13501
13502 return calcBezier(getTForX(aX), mY1, mY2);
13503 };
13504
13505 f.getControlPoints = function () {
13506 return [{
13507 x: mX1,
13508 y: mY1
13509 }, {
13510 x: mX2,
13511 y: mY2
13512 }];
13513 };
13514
13515 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
13516
13517 f.toString = function () {
13518 return str;
13519 };
13520
13521 return f;
13522}
13523
13524/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13525
13526/* 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
13527 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
13528var generateSpringRK4 = function () {
13529 function springAccelerationForState(state) {
13530 return -state.tension * state.x - state.friction * state.v;
13531 }
13532
13533 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
13534 var state = {
13535 x: initialState.x + derivative.dx * dt,
13536 v: initialState.v + derivative.dv * dt,
13537 tension: initialState.tension,
13538 friction: initialState.friction
13539 };
13540 return {
13541 dx: state.v,
13542 dv: springAccelerationForState(state)
13543 };
13544 }
13545
13546 function springIntegrateState(state, dt) {
13547 var a = {
13548 dx: state.v,
13549 dv: springAccelerationForState(state)
13550 },
13551 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
13552 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
13553 d = springEvaluateStateWithDerivative(state, dt, c),
13554 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
13555 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
13556 state.x = state.x + dxdt * dt;
13557 state.v = state.v + dvdt * dt;
13558 return state;
13559 }
13560
13561 return function springRK4Factory(tension, friction, duration) {
13562 var initState = {
13563 x: -1,
13564 v: 0,
13565 tension: null,
13566 friction: null
13567 },
13568 path = [0],
13569 time_lapsed = 0,
13570 tolerance = 1 / 10000,
13571 DT = 16 / 1000,
13572 have_duration,
13573 dt,
13574 last_state;
13575 tension = parseFloat(tension) || 500;
13576 friction = parseFloat(friction) || 20;
13577 duration = duration || null;
13578 initState.tension = tension;
13579 initState.friction = friction;
13580 have_duration = duration !== null;
13581 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
13582
13583 if (have_duration) {
13584 /* Run the simulation without a duration. */
13585 time_lapsed = springRK4Factory(tension, friction);
13586 /* Compute the adjusted time delta. */
13587
13588 dt = time_lapsed / duration * DT;
13589 } else {
13590 dt = DT;
13591 }
13592
13593 for (;;) {
13594 /* Next/step function .*/
13595 last_state = springIntegrateState(last_state || initState, dt);
13596 /* Store the position. */
13597
13598 path.push(1 + last_state.x);
13599 time_lapsed += 16;
13600 /* If the change threshold is reached, break. */
13601
13602 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
13603 break;
13604 }
13605 }
13606 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
13607 computed path and returns a snapshot of the position according to a given percentComplete. */
13608
13609
13610 return !have_duration ? time_lapsed : function (percentComplete) {
13611 return path[percentComplete * (path.length - 1) | 0];
13612 };
13613 };
13614}();
13615
13616var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
13617 var bezier = generateCubicBezier(t1, p1, t2, p2);
13618 return function (start, end, percent) {
13619 return start + (end - start) * bezier(percent);
13620 };
13621};
13622
13623var easings = {
13624 'linear': function linear(start, end, percent) {
13625 return start + (end - start) * percent;
13626 },
13627 // default easings
13628 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
13629 'ease-in': cubicBezier(0.42, 0, 1, 1),
13630 'ease-out': cubicBezier(0, 0, 0.58, 1),
13631 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
13632 // sine
13633 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
13634 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
13635 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
13636 // quad
13637 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
13638 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
13639 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
13640 // cubic
13641 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
13642 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
13643 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
13644 // quart
13645 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
13646 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
13647 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
13648 // quint
13649 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
13650 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
13651 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
13652 // expo
13653 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
13654 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
13655 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
13656 // circ
13657 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
13658 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
13659 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
13660 // user param easings...
13661 'spring': function spring(tension, friction, duration) {
13662 if (duration === 0) {
13663 // can't get a spring w/ duration 0
13664 return easings.linear; // duration 0 => jump to end so impl doesn't matter
13665 }
13666
13667 var spring = generateSpringRK4(tension, friction, duration);
13668 return function (start, end, percent) {
13669 return start + (end - start) * spring(percent);
13670 };
13671 },
13672 'cubic-bezier': cubicBezier
13673};
13674
13675function getEasedValue(type, start, end, percent, easingFn) {
13676 if (percent === 1) {
13677 return end;
13678 }
13679
13680 if (start === end) {
13681 return end;
13682 }
13683
13684 var val = easingFn(start, end, percent);
13685
13686 if (type == null) {
13687 return val;
13688 }
13689
13690 if (type.roundValue || type.color) {
13691 val = Math.round(val);
13692 }
13693
13694 if (type.min !== undefined) {
13695 val = Math.max(val, type.min);
13696 }
13697
13698 if (type.max !== undefined) {
13699 val = Math.min(val, type.max);
13700 }
13701
13702 return val;
13703}
13704
13705function getValue(prop, spec) {
13706 if (prop.pfValue != null || prop.value != null) {
13707 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
13708 return prop.pfValue;
13709 } else {
13710 return prop.value;
13711 }
13712 } else {
13713 return prop;
13714 }
13715}
13716
13717function ease(startProp, endProp, percent, easingFn, propSpec) {
13718 var type = propSpec != null ? propSpec.type : null;
13719
13720 if (percent < 0) {
13721 percent = 0;
13722 } else if (percent > 1) {
13723 percent = 1;
13724 }
13725
13726 var start = getValue(startProp, propSpec);
13727 var end = getValue(endProp, propSpec);
13728
13729 if (number(start) && number(end)) {
13730 return getEasedValue(type, start, end, percent, easingFn);
13731 } else if (array(start) && array(end)) {
13732 var easedArr = [];
13733
13734 for (var i = 0; i < end.length; i++) {
13735 var si = start[i];
13736 var ei = end[i];
13737
13738 if (si != null && ei != null) {
13739 var val = getEasedValue(type, si, ei, percent, easingFn);
13740 easedArr.push(val);
13741 } else {
13742 easedArr.push(ei);
13743 }
13744 }
13745
13746 return easedArr;
13747 }
13748
13749 return undefined;
13750}
13751
13752function step(self, ani, now, isCore) {
13753 var isEles = !isCore;
13754 var _p = self._private;
13755 var ani_p = ani._private;
13756 var pEasing = ani_p.easing;
13757 var startTime = ani_p.startTime;
13758 var cy = isCore ? self : self.cy();
13759 var style = cy.style();
13760
13761 if (!ani_p.easingImpl) {
13762 if (pEasing == null) {
13763 // use default
13764 ani_p.easingImpl = easings['linear'];
13765 } else {
13766 // then define w/ name
13767 var easingVals;
13768
13769 if (string(pEasing)) {
13770 var easingProp = style.parse('transition-timing-function', pEasing);
13771 easingVals = easingProp.value;
13772 } else {
13773 // then assume preparsed array
13774 easingVals = pEasing;
13775 }
13776
13777 var name, args;
13778
13779 if (string(easingVals)) {
13780 name = easingVals;
13781 args = [];
13782 } else {
13783 name = easingVals[1];
13784 args = easingVals.slice(2).map(function (n) {
13785 return +n;
13786 });
13787 }
13788
13789 if (args.length > 0) {
13790 // create with args
13791 if (name === 'spring') {
13792 args.push(ani_p.duration); // need duration to generate spring
13793 }
13794
13795 ani_p.easingImpl = easings[name].apply(null, args);
13796 } else {
13797 // static impl by name
13798 ani_p.easingImpl = easings[name];
13799 }
13800 }
13801 }
13802
13803 var easing = ani_p.easingImpl;
13804 var percent;
13805
13806 if (ani_p.duration === 0) {
13807 percent = 1;
13808 } else {
13809 percent = (now - startTime) / ani_p.duration;
13810 }
13811
13812 if (ani_p.applying) {
13813 percent = ani_p.progress;
13814 }
13815
13816 if (percent < 0) {
13817 percent = 0;
13818 } else if (percent > 1) {
13819 percent = 1;
13820 }
13821
13822 if (ani_p.delay == null) {
13823 // then update
13824 var startPos = ani_p.startPosition;
13825 var endPos = ani_p.position;
13826
13827 if (endPos && isEles && !self.locked()) {
13828 var newPos = {};
13829
13830 if (valid(startPos.x, endPos.x)) {
13831 newPos.x = ease(startPos.x, endPos.x, percent, easing);
13832 }
13833
13834 if (valid(startPos.y, endPos.y)) {
13835 newPos.y = ease(startPos.y, endPos.y, percent, easing);
13836 }
13837
13838 self.position(newPos);
13839 }
13840
13841 var startPan = ani_p.startPan;
13842 var endPan = ani_p.pan;
13843 var pan = _p.pan;
13844 var animatingPan = endPan != null && isCore;
13845
13846 if (animatingPan) {
13847 if (valid(startPan.x, endPan.x)) {
13848 pan.x = ease(startPan.x, endPan.x, percent, easing);
13849 }
13850
13851 if (valid(startPan.y, endPan.y)) {
13852 pan.y = ease(startPan.y, endPan.y, percent, easing);
13853 }
13854
13855 self.emit('pan');
13856 }
13857
13858 var startZoom = ani_p.startZoom;
13859 var endZoom = ani_p.zoom;
13860 var animatingZoom = endZoom != null && isCore;
13861
13862 if (animatingZoom) {
13863 if (valid(startZoom, endZoom)) {
13864 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
13865 }
13866
13867 self.emit('zoom');
13868 }
13869
13870 if (animatingPan || animatingZoom) {
13871 self.emit('viewport');
13872 }
13873
13874 var props = ani_p.style;
13875
13876 if (props && props.length > 0 && isEles) {
13877 for (var i = 0; i < props.length; i++) {
13878 var prop = props[i];
13879 var _name = prop.name;
13880 var end = prop;
13881 var start = ani_p.startStyle[_name];
13882 var propSpec = style.properties[start.name];
13883 var easedVal = ease(start, end, percent, easing, propSpec);
13884 style.overrideBypass(self, _name, easedVal);
13885 } // for props
13886
13887
13888 self.emit('style');
13889 } // if
13890
13891 }
13892
13893 ani_p.progress = percent;
13894 return percent;
13895}
13896
13897function valid(start, end) {
13898 if (start == null || end == null) {
13899 return false;
13900 }
13901
13902 if (number(start) && number(end)) {
13903 return true;
13904 } else if (start && end) {
13905 return true;
13906 }
13907
13908 return false;
13909}
13910
13911function startAnimation(self, ani, now, isCore) {
13912 var ani_p = ani._private;
13913 ani_p.started = true;
13914 ani_p.startTime = now - ani_p.progress * ani_p.duration;
13915}
13916
13917function stepAll(now, cy) {
13918 var eles = cy._private.aniEles;
13919 var doneEles = [];
13920
13921 function stepOne(ele, isCore) {
13922 var _p = ele._private;
13923 var current = _p.animation.current;
13924 var queue = _p.animation.queue;
13925 var ranAnis = false; // if nothing currently animating, get something from the queue
13926
13927 if (current.length === 0) {
13928 var next = queue.shift();
13929
13930 if (next) {
13931 current.push(next);
13932 }
13933 }
13934
13935 var callbacks = function callbacks(_callbacks) {
13936 for (var j = _callbacks.length - 1; j >= 0; j--) {
13937 var cb = _callbacks[j];
13938 cb();
13939 }
13940
13941 _callbacks.splice(0, _callbacks.length);
13942 }; // step and remove if done
13943
13944
13945 for (var i = current.length - 1; i >= 0; i--) {
13946 var ani = current[i];
13947 var ani_p = ani._private;
13948
13949 if (ani_p.stopped) {
13950 current.splice(i, 1);
13951 ani_p.hooked = false;
13952 ani_p.playing = false;
13953 ani_p.started = false;
13954 callbacks(ani_p.frames);
13955 continue;
13956 }
13957
13958 if (!ani_p.playing && !ani_p.applying) {
13959 continue;
13960 } // an apply() while playing shouldn't do anything
13961
13962
13963 if (ani_p.playing && ani_p.applying) {
13964 ani_p.applying = false;
13965 }
13966
13967 if (!ani_p.started) {
13968 startAnimation(ele, ani, now);
13969 }
13970
13971 step(ele, ani, now, isCore);
13972
13973 if (ani_p.applying) {
13974 ani_p.applying = false;
13975 }
13976
13977 callbacks(ani_p.frames);
13978
13979 if (ani_p.step != null) {
13980 ani_p.step(now);
13981 }
13982
13983 if (ani.completed()) {
13984 current.splice(i, 1);
13985 ani_p.hooked = false;
13986 ani_p.playing = false;
13987 ani_p.started = false;
13988 callbacks(ani_p.completes);
13989 }
13990
13991 ranAnis = true;
13992 }
13993
13994 if (!isCore && current.length === 0 && queue.length === 0) {
13995 doneEles.push(ele);
13996 }
13997
13998 return ranAnis;
13999 } // stepElement
14000 // handle all eles
14001
14002
14003 var ranEleAni = false;
14004
14005 for (var e = 0; e < eles.length; e++) {
14006 var ele = eles[e];
14007 var handledThisEle = stepOne(ele);
14008 ranEleAni = ranEleAni || handledThisEle;
14009 } // each element
14010
14011
14012 var ranCoreAni = stepOne(cy, true); // notify renderer
14013
14014 if (ranEleAni || ranCoreAni) {
14015 if (eles.length > 0) {
14016 cy.notify('draw', eles);
14017 } else {
14018 cy.notify('draw');
14019 }
14020 } // remove elements from list of currently animating if its queues are empty
14021
14022
14023 eles.unmerge(doneEles);
14024 cy.emit('step');
14025} // stepAll
14026
14027var corefn$1 = {
14028 // pull in animation functions
14029 animate: define$3.animate(),
14030 animation: define$3.animation(),
14031 animated: define$3.animated(),
14032 clearQueue: define$3.clearQueue(),
14033 delay: define$3.delay(),
14034 delayAnimation: define$3.delayAnimation(),
14035 stop: define$3.stop(),
14036 addToAnimationPool: function addToAnimationPool(eles) {
14037 var cy = this;
14038
14039 if (!cy.styleEnabled()) {
14040 return;
14041 } // save cycles when no style used
14042
14043
14044 cy._private.aniEles.merge(eles);
14045 },
14046 stopAnimationLoop: function stopAnimationLoop() {
14047 this._private.animationsRunning = false;
14048 },
14049 startAnimationLoop: function startAnimationLoop() {
14050 var cy = this;
14051 cy._private.animationsRunning = true;
14052
14053 if (!cy.styleEnabled()) {
14054 return;
14055 } // save cycles when no style used
14056 // NB the animation loop will exec in headless environments if style enabled
14057 // and explicit cy.destroy() is necessary to stop the loop
14058
14059
14060 function headlessStep() {
14061 if (!cy._private.animationsRunning) {
14062 return;
14063 }
14064
14065 requestAnimationFrame(function animationStep(now) {
14066 stepAll(now, cy);
14067 headlessStep();
14068 });
14069 }
14070
14071 var renderer = cy.renderer();
14072
14073 if (renderer && renderer.beforeRender) {
14074 // let the renderer schedule animations
14075 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14076 stepAll(now, cy);
14077 }, renderer.beforeRenderPriorities.animations);
14078 } else {
14079 // manage the animation loop ourselves
14080 headlessStep(); // first call
14081 }
14082 }
14083};
14084
14085var emitterOptions$1 = {
14086 qualifierCompare: function qualifierCompare(selector1, selector2) {
14087 if (selector1 == null || selector2 == null) {
14088 return selector1 == null && selector2 == null;
14089 } else {
14090 return selector1.sameText(selector2);
14091 }
14092 },
14093 eventMatches: function eventMatches(cy, listener, eventObj) {
14094 var selector = listener.qualifier;
14095
14096 if (selector != null) {
14097 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14098 }
14099
14100 return true;
14101 },
14102 addEventFields: function addEventFields(cy, evt) {
14103 evt.cy = cy;
14104 evt.target = cy;
14105 },
14106 callbackContext: function callbackContext(cy, listener, eventObj) {
14107 return listener.qualifier != null ? eventObj.target : cy;
14108 }
14109};
14110
14111var argSelector$1 = function argSelector(arg) {
14112 if (string(arg)) {
14113 return new Selector(arg);
14114 } else {
14115 return arg;
14116 }
14117};
14118
14119var elesfn$v = {
14120 createEmitter: function createEmitter() {
14121 var _p = this._private;
14122
14123 if (!_p.emitter) {
14124 _p.emitter = new Emitter(emitterOptions$1, this);
14125 }
14126
14127 return this;
14128 },
14129 emitter: function emitter() {
14130 return this._private.emitter;
14131 },
14132 on: function on(events, selector, callback) {
14133 this.emitter().on(events, argSelector$1(selector), callback);
14134 return this;
14135 },
14136 removeListener: function removeListener(events, selector, callback) {
14137 this.emitter().removeListener(events, argSelector$1(selector), callback);
14138 return this;
14139 },
14140 removeAllListeners: function removeAllListeners() {
14141 this.emitter().removeAllListeners();
14142 return this;
14143 },
14144 one: function one(events, selector, callback) {
14145 this.emitter().one(events, argSelector$1(selector), callback);
14146 return this;
14147 },
14148 once: function once(events, selector, callback) {
14149 this.emitter().one(events, argSelector$1(selector), callback);
14150 return this;
14151 },
14152 emit: function emit(events, extraParams) {
14153 this.emitter().emit(events, extraParams);
14154 return this;
14155 },
14156 emitAndNotify: function emitAndNotify(event, eles) {
14157 this.emit(event);
14158 this.notify(event, eles);
14159 return this;
14160 }
14161};
14162define$3.eventAliasesOn(elesfn$v);
14163
14164var corefn$2 = {
14165 png: function png(options) {
14166 var renderer = this._private.renderer;
14167 options = options || {};
14168 return renderer.png(options);
14169 },
14170 jpg: function jpg(options) {
14171 var renderer = this._private.renderer;
14172 options = options || {};
14173 options.bg = options.bg || '#fff';
14174 return renderer.jpg(options);
14175 }
14176};
14177corefn$2.jpeg = corefn$2.jpg;
14178
14179var corefn$3 = {
14180 layout: function layout(options) {
14181 var cy = this;
14182
14183 if (options == null) {
14184 error('Layout options must be specified to make a layout');
14185 return;
14186 }
14187
14188 if (options.name == null) {
14189 error('A `name` must be specified to make a layout');
14190 return;
14191 }
14192
14193 var name = options.name;
14194 var Layout = cy.extension('layout', name);
14195
14196 if (Layout == null) {
14197 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14198 return;
14199 }
14200
14201 var eles;
14202
14203 if (string(options.eles)) {
14204 eles = cy.$(options.eles);
14205 } else {
14206 eles = options.eles != null ? options.eles : cy.$();
14207 }
14208
14209 var layout = new Layout(extend({}, options, {
14210 cy: cy,
14211 eles: eles
14212 }));
14213 return layout;
14214 }
14215};
14216corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
14217
14218var corefn$4 = {
14219 notify: function notify(eventName, eventEles) {
14220 var _p = this._private;
14221
14222 if (this.batching()) {
14223 _p.batchNotifications = _p.batchNotifications || {};
14224 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14225
14226 if (eventEles != null) {
14227 eles.merge(eventEles);
14228 }
14229
14230 return; // notifications are disabled during batching
14231 }
14232
14233 if (!_p.notificationsEnabled) {
14234 return;
14235 } // exit on disabled
14236
14237
14238 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
14239
14240 if (this.destroyed() || !renderer) {
14241 return;
14242 }
14243
14244 renderer.notify(eventName, eventEles);
14245 },
14246 notifications: function notifications(bool) {
14247 var p = this._private;
14248
14249 if (bool === undefined) {
14250 return p.notificationsEnabled;
14251 } else {
14252 p.notificationsEnabled = bool ? true : false;
14253 }
14254
14255 return this;
14256 },
14257 noNotifications: function noNotifications(callback) {
14258 this.notifications(false);
14259 callback();
14260 this.notifications(true);
14261 },
14262 batching: function batching() {
14263 return this._private.batchCount > 0;
14264 },
14265 startBatch: function startBatch() {
14266 var _p = this._private;
14267
14268 if (_p.batchCount == null) {
14269 _p.batchCount = 0;
14270 }
14271
14272 if (_p.batchCount === 0) {
14273 _p.batchStyleEles = this.collection();
14274 _p.batchNotifications = {};
14275 }
14276
14277 _p.batchCount++;
14278 return this;
14279 },
14280 endBatch: function endBatch() {
14281 var _p = this._private;
14282
14283 if (_p.batchCount === 0) {
14284 return this;
14285 }
14286
14287 _p.batchCount--;
14288
14289 if (_p.batchCount === 0) {
14290 // update style for dirty eles
14291 _p.batchStyleEles.updateStyle();
14292
14293 var renderer = this.renderer(); // notify the renderer of queued eles and event types
14294
14295 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14296 var eles = _p.batchNotifications[eventName];
14297
14298 if (eles.empty()) {
14299 renderer.notify(eventName);
14300 } else {
14301 renderer.notify(eventName, eles);
14302 }
14303 });
14304 }
14305
14306 return this;
14307 },
14308 batch: function batch(callback) {
14309 this.startBatch();
14310 callback();
14311 this.endBatch();
14312 return this;
14313 },
14314 // for backwards compatibility
14315 batchData: function batchData(map) {
14316 var cy = this;
14317 return this.batch(function () {
14318 var ids = Object.keys(map);
14319
14320 for (var i = 0; i < ids.length; i++) {
14321 var id = ids[i];
14322 var data = map[id];
14323 var ele = cy.getElementById(id);
14324 ele.data(data);
14325 }
14326 });
14327 }
14328};
14329
14330var rendererDefaults = defaults({
14331 hideEdgesOnViewport: false,
14332 textureOnViewport: false,
14333 motionBlur: false,
14334 motionBlurOpacity: 0.05,
14335 pixelRatio: undefined,
14336 desktopTapThreshold: 4,
14337 touchTapThreshold: 8,
14338 wheelSensitivity: 1,
14339 debug: false,
14340 showFps: false
14341});
14342var corefn$5 = {
14343 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14344 var r = this._private.renderer;
14345 r.renderTo(context, zoom, pan, pxRatio);
14346 return this;
14347 },
14348 renderer: function renderer() {
14349 return this._private.renderer;
14350 },
14351 forceRender: function forceRender() {
14352 this.notify('draw');
14353 return this;
14354 },
14355 resize: function resize() {
14356 this.invalidateSize();
14357 this.emitAndNotify('resize');
14358 return this;
14359 },
14360 initRenderer: function initRenderer(options) {
14361 var cy = this;
14362 var RendererProto = cy.extension('renderer', options.name);
14363
14364 if (RendererProto == null) {
14365 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14366 return;
14367 }
14368
14369 if (options.wheelSensitivity !== undefined) {
14370 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.");
14371 }
14372
14373 var rOpts = rendererDefaults(options);
14374 rOpts.cy = cy;
14375 cy._private.renderer = new RendererProto(rOpts);
14376 this.notify('init');
14377 },
14378 destroyRenderer: function destroyRenderer() {
14379 var cy = this;
14380 cy.notify('destroy'); // destroy the renderer
14381
14382 var domEle = cy.container();
14383
14384 if (domEle) {
14385 domEle._cyreg = null;
14386
14387 while (domEle.childNodes.length > 0) {
14388 domEle.removeChild(domEle.childNodes[0]);
14389 }
14390 }
14391
14392 cy._private.renderer = null; // to be extra safe, remove the ref
14393
14394 cy.mutableElements().forEach(function (ele) {
14395 var _p = ele._private;
14396 _p.rscratch = {};
14397 _p.rstyle = {};
14398 _p.animation.current = [];
14399 _p.animation.queue = [];
14400 });
14401 },
14402 onRender: function onRender(fn) {
14403 return this.on('render', fn);
14404 },
14405 offRender: function offRender(fn) {
14406 return this.off('render', fn);
14407 }
14408};
14409corefn$5.invalidateDimensions = corefn$5.resize;
14410
14411var corefn$6 = {
14412 // get a collection
14413 // - empty collection on no args
14414 // - collection of elements in the graph on selector arg
14415 // - guarantee a returned collection when elements or collection specified
14416 collection: function collection(eles, opts) {
14417 if (string(eles)) {
14418 return this.$(eles);
14419 } else if (elementOrCollection(eles)) {
14420 return eles.collection();
14421 } else if (array(eles)) {
14422 return new Collection(this, eles, opts);
14423 }
14424
14425 return new Collection(this);
14426 },
14427 nodes: function nodes(selector) {
14428 var nodes = this.$(function (ele) {
14429 return ele.isNode();
14430 });
14431
14432 if (selector) {
14433 return nodes.filter(selector);
14434 }
14435
14436 return nodes;
14437 },
14438 edges: function edges(selector) {
14439 var edges = this.$(function (ele) {
14440 return ele.isEdge();
14441 });
14442
14443 if (selector) {
14444 return edges.filter(selector);
14445 }
14446
14447 return edges;
14448 },
14449 // search the graph like jQuery
14450 $: function $(selector) {
14451 var eles = this._private.elements;
14452
14453 if (selector) {
14454 return eles.filter(selector);
14455 } else {
14456 return eles.spawnSelf();
14457 }
14458 },
14459 mutableElements: function mutableElements() {
14460 return this._private.elements;
14461 }
14462}; // aliases
14463
14464corefn$6.elements = corefn$6.filter = corefn$6.$;
14465
14466var styfn = {}; // keys for style blocks, e.g. ttfftt
14467
14468var TRUE = 't';
14469var FALSE = 'f'; // (potentially expensive calculation)
14470// apply the style to the element based on
14471// - its bypass
14472// - what selectors match it
14473
14474styfn.apply = function (eles) {
14475 var self = this;
14476 var _p = self._private;
14477 var cy = _p.cy;
14478 var updatedEles = cy.collection();
14479
14480 for (var ie = 0; ie < eles.length; ie++) {
14481 var ele = eles[ie];
14482 var cxtMeta = self.getContextMeta(ele);
14483
14484 if (cxtMeta.empty) {
14485 continue;
14486 }
14487
14488 var cxtStyle = self.getContextStyle(cxtMeta);
14489 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
14490
14491 if (ele._private.appliedInitStyle) {
14492 self.updateTransitions(ele, app.diffProps);
14493 } else {
14494 ele._private.appliedInitStyle = true;
14495 }
14496
14497 var hintsDiff = self.updateStyleHints(ele);
14498
14499 if (hintsDiff) {
14500 updatedEles.push(ele);
14501 }
14502 } // for elements
14503
14504
14505 return updatedEles;
14506};
14507
14508styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
14509 var self = this;
14510 var cache = self._private.propDiffs = self._private.propDiffs || {};
14511 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
14512 var cachedVal = cache[dualCxtKey];
14513
14514 if (cachedVal) {
14515 return cachedVal;
14516 }
14517
14518 var diffProps = [];
14519 var addedProp = {};
14520
14521 for (var i = 0; i < self.length; i++) {
14522 var cxt = self[i];
14523 var oldHasCxt = oldCxtKey[i] === TRUE;
14524 var newHasCxt = newCxtKey[i] === TRUE;
14525 var cxtHasDiffed = oldHasCxt !== newHasCxt;
14526 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
14527
14528 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
14529 var props = void 0;
14530
14531 if (cxtHasDiffed && cxtHasMappedProps) {
14532 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
14533 } else if (cxtHasDiffed) {
14534 props = cxt.properties; // need to check them all
14535 } else if (cxtHasMappedProps) {
14536 props = cxt.mappedProperties; // only need to check mapped
14537 }
14538
14539 for (var j = 0; j < props.length; j++) {
14540 var prop = props[j];
14541 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
14542 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
14543 // is cached)
14544
14545 var laterCxtOverrides = false;
14546
14547 for (var k = i + 1; k < self.length; k++) {
14548 var laterCxt = self[k];
14549 var hasLaterCxt = newCxtKey[k] === TRUE;
14550
14551 if (!hasLaterCxt) {
14552 continue;
14553 } // can't override unless the context is active
14554
14555
14556 laterCxtOverrides = laterCxt.properties[prop.name] != null;
14557
14558 if (laterCxtOverrides) {
14559 break;
14560 } // exit early as long as one later context overrides
14561
14562 }
14563
14564 if (!addedProp[name] && !laterCxtOverrides) {
14565 addedProp[name] = true;
14566 diffProps.push(name);
14567 }
14568 } // for props
14569
14570 } // if
14571
14572 } // for contexts
14573
14574
14575 cache[dualCxtKey] = diffProps;
14576 return diffProps;
14577};
14578
14579styfn.getContextMeta = function (ele) {
14580 var self = this;
14581 var cxtKey = '';
14582 var diffProps;
14583 var prevKey = ele._private.styleCxtKey || ''; // get the cxt key
14584
14585 for (var i = 0; i < self.length; i++) {
14586 var context = self[i];
14587 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
14588
14589 if (contextSelectorMatches) {
14590 cxtKey += TRUE;
14591 } else {
14592 cxtKey += FALSE;
14593 }
14594 } // for context
14595
14596
14597 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
14598 ele._private.styleCxtKey = cxtKey;
14599 return {
14600 key: cxtKey,
14601 diffPropNames: diffProps,
14602 empty: diffProps.length === 0
14603 };
14604}; // gets a computed ele style object based on matched contexts
14605
14606
14607styfn.getContextStyle = function (cxtMeta) {
14608 var cxtKey = cxtMeta.key;
14609 var self = this;
14610 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
14611
14612 if (cxtStyles[cxtKey]) {
14613 return cxtStyles[cxtKey];
14614 }
14615
14616 var style = {
14617 _private: {
14618 key: cxtKey
14619 }
14620 };
14621
14622 for (var i = 0; i < self.length; i++) {
14623 var cxt = self[i];
14624 var hasCxt = cxtKey[i] === TRUE;
14625
14626 if (!hasCxt) {
14627 continue;
14628 }
14629
14630 for (var j = 0; j < cxt.properties.length; j++) {
14631 var prop = cxt.properties[j];
14632 style[prop.name] = prop;
14633 }
14634 }
14635
14636 cxtStyles[cxtKey] = style;
14637 return style;
14638};
14639
14640styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
14641 var self = this;
14642 var diffProps = cxtMeta.diffPropNames;
14643 var retDiffProps = {};
14644 var types = self.types;
14645
14646 for (var i = 0; i < diffProps.length; i++) {
14647 var diffPropName = diffProps[i];
14648 var cxtProp = cxtStyle[diffPropName];
14649 var eleProp = ele.pstyle(diffPropName);
14650
14651 if (!cxtProp) {
14652 // no context prop means delete
14653 if (!eleProp) {
14654 continue; // no existing prop means nothing needs to be removed
14655 // nb affects initial application on mapped values like control-point-distances
14656 } else if (eleProp.bypass) {
14657 cxtProp = {
14658 name: diffPropName,
14659 deleteBypassed: true
14660 };
14661 } else {
14662 cxtProp = {
14663 name: diffPropName,
14664 "delete": true
14665 };
14666 }
14667 } // save cycles when the context prop doesn't need to be applied
14668
14669
14670 if (eleProp === cxtProp) {
14671 continue;
14672 } // save cycles when a mapped context prop doesn't need to be applied
14673
14674
14675 if (cxtProp.mapped === types.fn // context prop is function mapper
14676 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
14677 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
14678 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
14679 ) {
14680 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
14681 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
14682
14683 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
14684
14685 if (fnValue === mapping.prevFnValue) {
14686 continue;
14687 }
14688 }
14689
14690 var retDiffProp = retDiffProps[diffPropName] = {
14691 prev: eleProp
14692 };
14693 self.applyParsedProperty(ele, cxtProp);
14694 retDiffProp.next = ele.pstyle(diffPropName);
14695
14696 if (retDiffProp.next && retDiffProp.next.bypass) {
14697 retDiffProp.next = retDiffProp.next.bypassed;
14698 }
14699 }
14700
14701 return {
14702 diffProps: retDiffProps
14703 };
14704};
14705
14706styfn.updateStyleHints = function (ele) {
14707 var _p = ele._private;
14708 var self = this;
14709 var propNames = self.propertyGroupNames;
14710 var propGrKeys = self.propertyGroupKeys;
14711
14712 var propHash = function propHash(ele, propNames, seedKey) {
14713 return self.getPropertiesHash(ele, propNames, seedKey);
14714 };
14715
14716 var oldStyleKey = _p.styleKey;
14717
14718 if (ele.removed()) {
14719 return false;
14720 }
14721
14722 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
14723 // but lazily -- only use non-default prop values to reduce the number of hashes
14724 //
14725
14726 var overriddenStyles = ele._private.style;
14727 propNames = Object.keys(overriddenStyles);
14728
14729 for (var i = 0; i < propGrKeys.length; i++) {
14730 var grKey = propGrKeys[i];
14731 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14732 }
14733
14734 var updateGrKey1 = function updateGrKey1(val, grKey) {
14735 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
14736 };
14737
14738 var updateGrKey2 = function updateGrKey2(val, grKey) {
14739 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
14740 };
14741
14742 var updateGrKey = function updateGrKey(val, grKey) {
14743 updateGrKey1(val, grKey);
14744 updateGrKey2(val, grKey);
14745 };
14746
14747 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
14748 for (var j = 0; j < strVal.length; j++) {
14749 var ch = strVal.charCodeAt(j);
14750 updateGrKey1(ch, grKey);
14751 updateGrKey2(ch, grKey);
14752 }
14753 }; // - hashing works on 32 bit ints b/c we use bitwise ops
14754 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
14755 // - raise up small numbers so more significant digits are seen by hashing
14756 // - make small numbers larger than a normal value to avoid collisions
14757 // - works in practice and it's relatively cheap
14758
14759
14760 var N = 2000000000;
14761
14762 var cleanNum = function cleanNum(val) {
14763 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
14764 };
14765
14766 for (var _i = 0; _i < propNames.length; _i++) {
14767 var name = propNames[_i];
14768 var parsedProp = overriddenStyles[name];
14769
14770 if (parsedProp == null) {
14771 continue;
14772 }
14773
14774 var propInfo = this.properties[name];
14775 var type = propInfo.type;
14776 var _grKey = propInfo.groupKey;
14777 var normalizedNumberVal = void 0;
14778
14779 if (propInfo.hashOverride != null) {
14780 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
14781 } else if (parsedProp.pfValue != null) {
14782 normalizedNumberVal = parsedProp.pfValue;
14783 } // might not be a number if it allows enums
14784
14785
14786 var numberVal = propInfo.enums == null ? parsedProp.value : null;
14787 var haveNormNum = normalizedNumberVal != null;
14788 var haveUnitedNum = numberVal != null;
14789 var haveNum = haveNormNum || haveUnitedNum;
14790 var units = parsedProp.units; // numbers are cheaper to hash than strings
14791 // 1 hash op vs n hash ops (for length n string)
14792
14793 if (type.number && haveNum && !type.multiple) {
14794 var v = haveNormNum ? normalizedNumberVal : numberVal;
14795 updateGrKey(cleanNum(v), _grKey);
14796
14797 if (!haveNormNum && units != null) {
14798 updateGrKeyWStr(units, _grKey);
14799 }
14800 } else {
14801 updateGrKeyWStr(parsedProp.strValue, _grKey);
14802 }
14803 } // overall style key
14804 //
14805
14806
14807 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14808
14809 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
14810 var _grKey2 = propGrKeys[_i2];
14811 var grHash = _p.styleKeys[_grKey2];
14812 hash[0] = hashInt(grHash[0], hash[0]);
14813 hash[1] = hashIntAlt(grHash[1], hash[1]);
14814 }
14815
14816 _p.styleKey = combineHashes(hash[0], hash[1]); // label dims
14817 //
14818
14819 var sk = _p.styleKeys;
14820 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
14821 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
14822 _p.labelKey = combineHashesArray(labelKeys);
14823 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
14824
14825 if (!isNode) {
14826 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
14827 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
14828 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
14829 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
14830 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
14831 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
14832 } // node
14833 //
14834
14835
14836 if (isNode) {
14837 var _p$styleKeys = _p.styleKeys,
14838 nodeBody = _p$styleKeys.nodeBody,
14839 nodeBorder = _p$styleKeys.nodeBorder,
14840 backgroundImage = _p$styleKeys.backgroundImage,
14841 compound = _p$styleKeys.compound,
14842 pie = _p$styleKeys.pie;
14843 var nodeKeys = [nodeBody, nodeBorder, backgroundImage, compound, pie].filter(function (k) {
14844 return k != null;
14845 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
14846 _p.nodeKey = combineHashesArray(nodeKeys);
14847 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
14848 }
14849
14850 return oldStyleKey !== _p.styleKey;
14851};
14852
14853styfn.clearStyleHints = function (ele) {
14854 var _p = ele._private;
14855 _p.styleCxtKey = '';
14856 _p.styleKeys = {};
14857 _p.styleKey = null;
14858 _p.labelKey = null;
14859 _p.labelStyleKey = null;
14860 _p.sourceLabelKey = null;
14861 _p.sourceLabelStyleKey = null;
14862 _p.targetLabelKey = null;
14863 _p.targetLabelStyleKey = null;
14864 _p.nodeKey = null;
14865 _p.hasPie = null;
14866}; // apply a property to the style (for internal use)
14867// returns whether application was successful
14868//
14869// now, this function flattens the property, and here's how:
14870//
14871// for parsedProp:{ bypass: true, deleteBypass: true }
14872// no property is generated, instead the bypass property in the
14873// element's style is replaced by what's pointed to by the `bypassed`
14874// field in the bypass property (i.e. restoring the property the
14875// bypass was overriding)
14876//
14877// for parsedProp:{ mapped: truthy }
14878// the generated flattenedProp:{ mapping: prop }
14879//
14880// for parsedProp:{ bypass: true }
14881// the generated flattenedProp:{ bypassed: parsedProp }
14882
14883
14884styfn.applyParsedProperty = function (ele, parsedProp) {
14885 var self = this;
14886 var prop = parsedProp;
14887 var style = ele._private.style;
14888 var flatProp;
14889 var types = self.types;
14890 var type = self.properties[prop.name].type;
14891 var propIsBypass = prop.bypass;
14892 var origProp = style[prop.name];
14893 var origPropIsBypass = origProp && origProp.bypass;
14894 var _p = ele._private;
14895 var flatPropMapping = 'mapping';
14896
14897 var getVal = function getVal(p) {
14898 if (p == null) {
14899 return null;
14900 } else if (p.pfValue != null) {
14901 return p.pfValue;
14902 } else {
14903 return p.value;
14904 }
14905 };
14906
14907 var checkTriggers = function checkTriggers() {
14908 var fromVal = getVal(origProp);
14909 var toVal = getVal(prop);
14910 self.checkTriggers(ele, prop.name, fromVal, toVal);
14911 };
14912
14913 if (prop && prop.name.substr(0, 3) === 'pie') {
14914 warn('The pie style properties are deprecated. Create charts using background images instead.');
14915 } // edge sanity checks to prevent the client from making serious mistakes
14916
14917
14918 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
14919 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
14920 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
14921 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
14922 }
14923
14924 if (prop["delete"]) {
14925 // delete the property and use the default value on falsey value
14926 style[prop.name] = undefined;
14927 checkTriggers();
14928 return true;
14929 }
14930
14931 if (prop.deleteBypassed) {
14932 // delete the property that the
14933 if (!origProp) {
14934 checkTriggers();
14935 return true; // can't delete if no prop
14936 } else if (origProp.bypass) {
14937 // delete bypassed
14938 origProp.bypassed = undefined;
14939 checkTriggers();
14940 return true;
14941 } else {
14942 return false; // we're unsuccessful deleting the bypassed
14943 }
14944 } // check if we need to delete the current bypass
14945
14946
14947 if (prop.deleteBypass) {
14948 // then this property is just here to indicate we need to delete
14949 if (!origProp) {
14950 checkTriggers();
14951 return true; // property is already not defined
14952 } else if (origProp.bypass) {
14953 // then replace the bypass property with the original
14954 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
14955 style[prop.name] = origProp.bypassed;
14956 checkTriggers();
14957 return true;
14958 } else {
14959 return false; // we're unsuccessful deleting the bypass
14960 }
14961 }
14962
14963 var printMappingErr = function printMappingErr() {
14964 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');
14965 }; // put the property in the style objects
14966
14967
14968 switch (prop.mapped) {
14969 // flatten the property if mapped
14970 case types.mapData:
14971 {
14972 // flatten the field (e.g. data.foo.bar)
14973 var fields = prop.field.split('.');
14974 var fieldVal = _p.data;
14975
14976 for (var i = 0; i < fields.length && fieldVal; i++) {
14977 var field = fields[i];
14978 fieldVal = fieldVal[field];
14979 }
14980
14981 if (fieldVal == null) {
14982 printMappingErr();
14983 return false;
14984 }
14985
14986 var percent;
14987
14988 if (!number(fieldVal)) {
14989 // then don't apply and fall back on the existing style
14990 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
14991 return false;
14992 } else {
14993 var fieldWidth = prop.fieldMax - prop.fieldMin;
14994
14995 if (fieldWidth === 0) {
14996 // safety check -- not strictly necessary as no props of zero range should be passed here
14997 percent = 0;
14998 } else {
14999 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15000 }
15001 } // make sure to bound percent value
15002
15003
15004 if (percent < 0) {
15005 percent = 0;
15006 } else if (percent > 1) {
15007 percent = 1;
15008 }
15009
15010 if (type.color) {
15011 var r1 = prop.valueMin[0];
15012 var r2 = prop.valueMax[0];
15013 var g1 = prop.valueMin[1];
15014 var g2 = prop.valueMax[1];
15015 var b1 = prop.valueMin[2];
15016 var b2 = prop.valueMax[2];
15017 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15018 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15019 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)];
15020 flatProp = {
15021 // colours are simple, so just create the flat property instead of expensive string parsing
15022 bypass: prop.bypass,
15023 // we're a bypass if the mapping property is a bypass
15024 name: prop.name,
15025 value: clr,
15026 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15027 };
15028 } else if (type.number) {
15029 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15030 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15031 } else {
15032 return false; // can only map to colours and numbers
15033 }
15034
15035 if (!flatProp) {
15036 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15037 printMappingErr();
15038 return false;
15039 }
15040
15041 flatProp.mapping = prop; // keep a reference to the mapping
15042
15043 prop = flatProp; // the flattened (mapped) property is the one we want
15044
15045 break;
15046 }
15047 // direct mapping
15048
15049 case types.data:
15050 {
15051 // flatten the field (e.g. data.foo.bar)
15052 var _fields = prop.field.split('.');
15053
15054 var _fieldVal = _p.data;
15055
15056 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15057 var _field = _fields[_i3];
15058 _fieldVal = _fieldVal[_field];
15059 }
15060
15061 if (_fieldVal != null) {
15062 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15063 }
15064
15065 if (!flatProp) {
15066 // if we can't flatten the property, then don't apply and fall back on the existing style
15067 printMappingErr();
15068 return false;
15069 }
15070
15071 flatProp.mapping = prop; // keep a reference to the mapping
15072
15073 prop = flatProp; // the flattened (mapped) property is the one we want
15074
15075 break;
15076 }
15077
15078 case types.fn:
15079 {
15080 var fn = prop.value;
15081 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15082
15083 prop.prevFnValue = fnRetVal;
15084
15085 if (fnRetVal == null) {
15086 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15087 return false;
15088 }
15089
15090 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15091
15092 if (!flatProp) {
15093 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15094 return false;
15095 }
15096
15097 flatProp.mapping = copy(prop); // keep a reference to the mapping
15098
15099 prop = flatProp; // the flattened (mapped) property is the one we want
15100
15101 break;
15102 }
15103
15104 case undefined:
15105 break;
15106 // just set the property
15107
15108 default:
15109 return false;
15110 // not a valid mapping
15111 } // if the property is a bypass property, then link the resultant property to the original one
15112
15113
15114 if (propIsBypass) {
15115 if (origPropIsBypass) {
15116 // then this bypass overrides the existing one
15117 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15118 } else {
15119 // then link the orig prop to the new bypass
15120 prop.bypassed = origProp;
15121 }
15122
15123 style[prop.name] = prop; // and set
15124 } else {
15125 // prop is not bypass
15126 if (origPropIsBypass) {
15127 // then keep the orig prop (since it's a bypass) and link to the new prop
15128 origProp.bypassed = prop;
15129 } else {
15130 // then just replace the old prop with the new one
15131 style[prop.name] = prop;
15132 }
15133 }
15134
15135 checkTriggers();
15136 return true;
15137};
15138
15139styfn.cleanElements = function (eles, keepBypasses) {
15140 for (var i = 0; i < eles.length; i++) {
15141 var ele = eles[i];
15142 this.clearStyleHints(ele);
15143 ele.dirtyCompoundBoundsCache();
15144 ele.dirtyBoundingBoxCache();
15145
15146 if (!keepBypasses) {
15147 ele._private.style = {};
15148 } else {
15149 var style = ele._private.style;
15150 var propNames = Object.keys(style);
15151
15152 for (var j = 0; j < propNames.length; j++) {
15153 var propName = propNames[j];
15154 var eleProp = style[propName];
15155
15156 if (eleProp != null) {
15157 if (eleProp.bypass) {
15158 eleProp.bypassed = null;
15159 } else {
15160 style[propName] = null;
15161 }
15162 }
15163 }
15164 }
15165 }
15166}; // updates the visual style for all elements (useful for manual style modification after init)
15167
15168
15169styfn.update = function () {
15170 var cy = this._private.cy;
15171 var eles = cy.mutableElements();
15172 eles.updateStyle();
15173}; // diffProps : { name => { prev, next } }
15174
15175
15176styfn.updateTransitions = function (ele, diffProps) {
15177 var self = this;
15178 var _p = ele._private;
15179 var props = ele.pstyle('transition-property').value;
15180 var duration = ele.pstyle('transition-duration').pfValue;
15181 var delay = ele.pstyle('transition-delay').pfValue;
15182
15183 if (props.length > 0 && duration > 0) {
15184 var style = {}; // build up the style to animate towards
15185
15186 var anyPrev = false;
15187
15188 for (var i = 0; i < props.length; i++) {
15189 var prop = props[i];
15190 var styProp = ele.pstyle(prop);
15191 var diffProp = diffProps[prop];
15192
15193 if (!diffProp) {
15194 continue;
15195 }
15196
15197 var prevProp = diffProp.prev;
15198 var fromProp = prevProp;
15199 var toProp = diffProp.next != null ? diffProp.next : styProp;
15200 var diff = false;
15201 var initVal = void 0;
15202 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15203
15204 if (!fromProp) {
15205 continue;
15206 } // consider px values
15207
15208
15209 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
15210 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15211
15212 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
15213 } else if (number(fromProp.value) && number(toProp.value)) {
15214 diff = toProp.value - fromProp.value; // nonzero is truthy
15215
15216 initVal = fromProp.value + initDt * diff; // consider colour values
15217 } else if (array(fromProp.value) && array(toProp.value)) {
15218 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15219 initVal = fromProp.strValue;
15220 } // the previous value is good for an animation only if it's different
15221
15222
15223 if (diff) {
15224 style[prop] = toProp.strValue; // to val
15225
15226 this.applyBypass(ele, prop, initVal); // from val
15227
15228 anyPrev = true;
15229 }
15230 } // end if props allow ani
15231 // can't transition if there's nothing previous to transition from
15232
15233
15234 if (!anyPrev) {
15235 return;
15236 }
15237
15238 _p.transitioning = true;
15239 new Promise$1(function (resolve) {
15240 if (delay > 0) {
15241 ele.delayAnimation(delay).play().promise().then(resolve);
15242 } else {
15243 resolve();
15244 }
15245 }).then(function () {
15246 return ele.animation({
15247 style: style,
15248 duration: duration,
15249 easing: ele.pstyle('transition-timing-function').value,
15250 queue: false
15251 }).play().promise();
15252 }).then(function () {
15253 // if( !isBypass ){
15254 self.removeBypasses(ele, props);
15255 ele.emitAndNotify('style'); // }
15256
15257 _p.transitioning = false;
15258 });
15259 } else if (_p.transitioning) {
15260 this.removeBypasses(ele, props);
15261 ele.emitAndNotify('style');
15262 _p.transitioning = false;
15263 }
15264};
15265
15266styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15267 var prop = this.properties[name];
15268 var triggerCheck = getTrigger(prop);
15269
15270 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15271 onTrigger(prop);
15272 }
15273};
15274
15275styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15276 var _this = this;
15277
15278 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15279 return prop.triggersZOrder;
15280 }, function () {
15281 _this._private.cy.notify('zorder', ele);
15282 });
15283};
15284
15285styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15286 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15287 return prop.triggersBounds;
15288 }, function (prop) {
15289 ele.dirtyCompoundBoundsCache();
15290 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
15291 // then dirty the pll edge bb cache as well
15292
15293 if ( // only for beziers -- so performance of other edges isn't affected
15294 name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') && prop.triggersBoundsOfParallelBeziers) {
15295 ele.parallelEdges().forEach(function (pllEdge) {
15296 if (pllEdge.isBundledBezier()) {
15297 pllEdge.dirtyBoundingBoxCache();
15298 }
15299 });
15300 }
15301 });
15302};
15303
15304styfn.checkTriggers = function (ele, name, fromValue, toValue) {
15305 ele.dirtyStyleCache();
15306 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15307 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15308};
15309
15310var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
15311// returns true iff application was successful for at least 1 specified property
15312
15313styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
15314 var self = this;
15315 var props = [];
15316 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
15317
15318 if (name === '*' || name === '**') {
15319 // apply to all property names
15320 if (value !== undefined) {
15321 for (var i = 0; i < self.properties.length; i++) {
15322 var prop = self.properties[i];
15323 var _name = prop.name;
15324 var parsedProp = this.parse(_name, value, true);
15325
15326 if (parsedProp) {
15327 props.push(parsedProp);
15328 }
15329 }
15330 }
15331 } else if (string(name)) {
15332 // then parse the single property
15333 var _parsedProp = this.parse(name, value, true);
15334
15335 if (_parsedProp) {
15336 props.push(_parsedProp);
15337 }
15338 } else if (plainObject(name)) {
15339 // then parse each property
15340 var specifiedProps = name;
15341 updateTransitions = value;
15342 var names = Object.keys(specifiedProps);
15343
15344 for (var _i = 0; _i < names.length; _i++) {
15345 var _name2 = names[_i];
15346 var _value = specifiedProps[_name2];
15347
15348 if (_value === undefined) {
15349 // try camel case name too
15350 _value = specifiedProps[dash2camel(_name2)];
15351 }
15352
15353 if (_value !== undefined) {
15354 var _parsedProp2 = this.parse(_name2, _value, true);
15355
15356 if (_parsedProp2) {
15357 props.push(_parsedProp2);
15358 }
15359 }
15360 }
15361 } else {
15362 // can't do anything without well defined properties
15363 return false;
15364 } // we've failed if there are no valid properties
15365
15366
15367 if (props.length === 0) {
15368 return false;
15369 } // now, apply the bypass properties on the elements
15370
15371
15372 var ret = false; // return true if at least one succesful bypass applied
15373
15374 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15375 // for each ele
15376 var ele = eles[_i2];
15377 var diffProps = {};
15378 var diffProp = void 0;
15379
15380 for (var j = 0; j < props.length; j++) {
15381 // for each prop
15382 var _prop = props[j];
15383
15384 if (updateTransitions) {
15385 var prevProp = ele.pstyle(_prop.name);
15386 diffProp = diffProps[_prop.name] = {
15387 prev: prevProp
15388 };
15389 }
15390
15391 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
15392
15393 if (updateTransitions) {
15394 diffProp.next = ele.pstyle(_prop.name);
15395 }
15396 } // for props
15397
15398
15399 if (ret) {
15400 this.updateStyleHints(ele);
15401 }
15402
15403 if (updateTransitions) {
15404 this.updateTransitions(ele, diffProps, isBypass);
15405 }
15406 } // for eles
15407
15408
15409 return ret;
15410}; // only useful in specific cases like animation
15411
15412
15413styfn$1.overrideBypass = function (eles, name, value) {
15414 name = camel2dash(name);
15415
15416 for (var i = 0; i < eles.length; i++) {
15417 var ele = eles[i];
15418 var prop = ele._private.style[name];
15419 var type = this.properties[name].type;
15420 var isColor = type.color;
15421 var isMulti = type.mutiple;
15422 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15423
15424 if (!prop || !prop.bypass) {
15425 // need a bypass if one doesn't exist
15426 this.applyBypass(ele, name, value);
15427 } else {
15428 prop.value = value;
15429
15430 if (prop.pfValue != null) {
15431 prop.pfValue = value;
15432 }
15433
15434 if (isColor) {
15435 prop.strValue = 'rgb(' + value.join(',') + ')';
15436 } else if (isMulti) {
15437 prop.strValue = value.join(' ');
15438 } else {
15439 prop.strValue = '' + value;
15440 }
15441
15442 this.updateStyleHints(ele);
15443 }
15444
15445 this.checkTriggers(ele, name, oldValue, value);
15446 }
15447};
15448
15449styfn$1.removeAllBypasses = function (eles, updateTransitions) {
15450 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15451};
15452
15453styfn$1.removeBypasses = function (eles, props, updateTransitions) {
15454 var isBypass = true;
15455
15456 for (var j = 0; j < eles.length; j++) {
15457 var ele = eles[j];
15458 var diffProps = {};
15459
15460 for (var i = 0; i < props.length; i++) {
15461 var name = props[i];
15462 var prop = this.properties[name];
15463 var prevProp = ele.pstyle(prop.name);
15464
15465 if (!prevProp || !prevProp.bypass) {
15466 // if a bypass doesn't exist for the prop, nothing needs to be removed
15467 continue;
15468 }
15469
15470 var value = ''; // empty => remove bypass
15471
15472 var parsedProp = this.parse(name, value, true);
15473 var diffProp = diffProps[prop.name] = {
15474 prev: prevProp
15475 };
15476 this.applyParsedProperty(ele, parsedProp);
15477 diffProp.next = ele.pstyle(prop.name);
15478 } // for props
15479
15480
15481 this.updateStyleHints(ele);
15482
15483 if (updateTransitions) {
15484 this.updateTransitions(ele, diffProps, isBypass);
15485 }
15486 } // for eles
15487
15488};
15489
15490var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
15491
15492styfn$2.getEmSizeInPixels = function () {
15493 var px = this.containerCss('font-size');
15494
15495 if (px != null) {
15496 return parseFloat(px);
15497 } else {
15498 return 1; // for headless
15499 }
15500}; // gets css property from the core container
15501
15502
15503styfn$2.containerCss = function (propName) {
15504 var cy = this._private.cy;
15505 var domElement = cy.container();
15506
15507 if (window$1 && domElement && window$1.getComputedStyle) {
15508 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
15509 }
15510};
15511
15512var styfn$3 = {}; // gets the rendered style for an element
15513
15514styfn$3.getRenderedStyle = function (ele, prop) {
15515 if (prop) {
15516 return this.getStylePropertyValue(ele, prop, true);
15517 } else {
15518 return this.getRawStyle(ele, true);
15519 }
15520}; // gets the raw style for an element
15521
15522
15523styfn$3.getRawStyle = function (ele, isRenderedVal) {
15524 var self = this;
15525 ele = ele[0]; // insure it's an element
15526
15527 if (ele) {
15528 var rstyle = {};
15529
15530 for (var i = 0; i < self.properties.length; i++) {
15531 var prop = self.properties[i];
15532 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15533
15534 if (val != null) {
15535 rstyle[prop.name] = val;
15536 rstyle[dash2camel(prop.name)] = val;
15537 }
15538 }
15539
15540 return rstyle;
15541 }
15542};
15543
15544styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
15545 var pstyle = ele.pstyle(property)[subproperty][index];
15546 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15547};
15548
15549styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15550 var self = this;
15551 ele = ele[0]; // insure it's an element
15552
15553 if (ele) {
15554 var prop = self.properties[propName];
15555
15556 if (prop.alias) {
15557 prop = prop.pointsTo;
15558 }
15559
15560 var type = prop.type;
15561 var styleProp = ele.pstyle(prop.name);
15562
15563 if (styleProp) {
15564 var value = styleProp.value,
15565 units = styleProp.units,
15566 strValue = styleProp.strValue;
15567
15568 if (isRenderedVal && type.number && value != null && number(value)) {
15569 var zoom = ele.cy().zoom();
15570
15571 var getRenderedValue = function getRenderedValue(val) {
15572 return val * zoom;
15573 };
15574
15575 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15576 return getRenderedValue(val) + units;
15577 };
15578
15579 var isArrayValue = array(value);
15580 var haveUnits = isArrayValue ? units.every(function (u) {
15581 return u != null;
15582 }) : units != null;
15583
15584 if (haveUnits) {
15585 if (isArrayValue) {
15586 return value.map(function (v, i) {
15587 return getValueStringWithUnits(v, units[i]);
15588 }).join(' ');
15589 } else {
15590 return getValueStringWithUnits(value, units);
15591 }
15592 } else {
15593 if (isArrayValue) {
15594 return value.map(function (v) {
15595 return string(v) ? v : '' + getRenderedValue(v);
15596 }).join(' ');
15597 } else {
15598 return '' + getRenderedValue(value);
15599 }
15600 }
15601 } else if (strValue != null) {
15602 return strValue;
15603 }
15604 }
15605
15606 return null;
15607 }
15608};
15609
15610styfn$3.getAnimationStartStyle = function (ele, aniProps) {
15611 var rstyle = {};
15612
15613 for (var i = 0; i < aniProps.length; i++) {
15614 var aniProp = aniProps[i];
15615 var name = aniProp.name;
15616 var styleProp = ele.pstyle(name);
15617
15618 if (styleProp !== undefined) {
15619 // then make a prop of it
15620 if (plainObject(styleProp)) {
15621 styleProp = this.parse(name, styleProp.strValue);
15622 } else {
15623 styleProp = this.parse(name, styleProp);
15624 }
15625 }
15626
15627 if (styleProp) {
15628 rstyle[name] = styleProp;
15629 }
15630 }
15631
15632 return rstyle;
15633};
15634
15635styfn$3.getPropsList = function (propsObj) {
15636 var self = this;
15637 var rstyle = [];
15638 var style = propsObj;
15639 var props = self.properties;
15640
15641 if (style) {
15642 var names = Object.keys(style);
15643
15644 for (var i = 0; i < names.length; i++) {
15645 var name = names[i];
15646 var val = style[name];
15647 var prop = props[name] || props[camel2dash(name)];
15648 var styleProp = this.parse(prop.name, val);
15649
15650 if (styleProp) {
15651 rstyle.push(styleProp);
15652 }
15653 }
15654 }
15655
15656 return rstyle;
15657};
15658
15659styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
15660 var hash = seed.slice();
15661 var name, val, strVal, chVal;
15662 var i, j;
15663
15664 for (i = 0; i < propNames.length; i++) {
15665 name = propNames[i];
15666 val = ele.pstyle(name, false);
15667
15668 if (val == null) {
15669 continue;
15670 } else if (val.pfValue != null) {
15671 hash[0] = hashInt(chVal, hash[0]);
15672 hash[1] = hashIntAlt(chVal, hash[1]);
15673 } else {
15674 strVal = val.strValue;
15675
15676 for (j = 0; j < strVal.length; j++) {
15677 chVal = strVal.charCodeAt(j);
15678 hash[0] = hashInt(chVal, hash[0]);
15679 hash[1] = hashIntAlt(chVal, hash[1]);
15680 }
15681 }
15682 }
15683
15684 return hash;
15685};
15686
15687styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
15688
15689var styfn$4 = {};
15690
15691styfn$4.appendFromJson = function (json) {
15692 var style = this;
15693
15694 for (var i = 0; i < json.length; i++) {
15695 var context = json[i];
15696 var selector = context.selector;
15697 var props = context.style || context.css;
15698 var names = Object.keys(props);
15699 style.selector(selector); // apply selector
15700
15701 for (var j = 0; j < names.length; j++) {
15702 var name = names[j];
15703 var value = props[name];
15704 style.css(name, value); // apply property
15705 }
15706 }
15707
15708 return style;
15709}; // accessible cy.style() function
15710
15711
15712styfn$4.fromJson = function (json) {
15713 var style = this;
15714 style.resetToDefault();
15715 style.appendFromJson(json);
15716 return style;
15717}; // get json from cy.style() api
15718
15719
15720styfn$4.json = function () {
15721 var json = [];
15722
15723 for (var i = this.defaultLength; i < this.length; i++) {
15724 var cxt = this[i];
15725 var selector = cxt.selector;
15726 var props = cxt.properties;
15727 var css = {};
15728
15729 for (var j = 0; j < props.length; j++) {
15730 var prop = props[j];
15731 css[prop.name] = prop.strValue;
15732 }
15733
15734 json.push({
15735 selector: !selector ? 'core' : selector.toString(),
15736 style: css
15737 });
15738 }
15739
15740 return json;
15741};
15742
15743var styfn$5 = {};
15744
15745styfn$5.appendFromString = function (string) {
15746 var self = this;
15747 var style = this;
15748 var remaining = '' + string;
15749 var selAndBlockStr;
15750 var blockRem;
15751 var propAndValStr; // remove comments from the style string
15752
15753 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
15754
15755 function removeSelAndBlockFromRemaining() {
15756 // remove the parsed selector and block from the remaining text to parse
15757 if (remaining.length > selAndBlockStr.length) {
15758 remaining = remaining.substr(selAndBlockStr.length);
15759 } else {
15760 remaining = '';
15761 }
15762 }
15763
15764 function removePropAndValFromRem() {
15765 // remove the parsed property and value from the remaining block text to parse
15766 if (blockRem.length > propAndValStr.length) {
15767 blockRem = blockRem.substr(propAndValStr.length);
15768 } else {
15769 blockRem = '';
15770 }
15771 }
15772
15773 for (;;) {
15774 var nothingLeftToParse = remaining.match(/^\s*$/);
15775
15776 if (nothingLeftToParse) {
15777 break;
15778 }
15779
15780 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
15781
15782 if (!selAndBlock) {
15783 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
15784 break;
15785 }
15786
15787 selAndBlockStr = selAndBlock[0]; // parse the selector
15788
15789 var selectorStr = selAndBlock[1];
15790
15791 if (selectorStr !== 'core') {
15792 var selector = new Selector(selectorStr);
15793
15794 if (selector.invalid) {
15795 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
15796
15797 removeSelAndBlockFromRemaining();
15798 continue;
15799 }
15800 } // parse the block of properties and values
15801
15802
15803 var blockStr = selAndBlock[2];
15804 var invalidBlock = false;
15805 blockRem = blockStr;
15806 var props = [];
15807
15808 for (;;) {
15809 var _nothingLeftToParse = blockRem.match(/^\s*$/);
15810
15811 if (_nothingLeftToParse) {
15812 break;
15813 }
15814
15815 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
15816
15817 if (!propAndVal) {
15818 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
15819 invalidBlock = true;
15820 break;
15821 }
15822
15823 propAndValStr = propAndVal[0];
15824 var propStr = propAndVal[1];
15825 var valStr = propAndVal[2];
15826 var prop = self.properties[propStr];
15827
15828 if (!prop) {
15829 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
15830
15831 removePropAndValFromRem();
15832 continue;
15833 }
15834
15835 var parsedProp = style.parse(propStr, valStr);
15836
15837 if (!parsedProp) {
15838 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
15839
15840 removePropAndValFromRem();
15841 continue;
15842 }
15843
15844 props.push({
15845 name: propStr,
15846 val: valStr
15847 });
15848 removePropAndValFromRem();
15849 }
15850
15851 if (invalidBlock) {
15852 removeSelAndBlockFromRemaining();
15853 break;
15854 } // put the parsed block in the style
15855
15856
15857 style.selector(selectorStr);
15858
15859 for (var i = 0; i < props.length; i++) {
15860 var _prop = props[i];
15861 style.css(_prop.name, _prop.val);
15862 }
15863
15864 removeSelAndBlockFromRemaining();
15865 }
15866
15867 return style;
15868};
15869
15870styfn$5.fromString = function (string) {
15871 var style = this;
15872 style.resetToDefault();
15873 style.appendFromString(string);
15874 return style;
15875};
15876
15877var styfn$6 = {};
15878
15879(function () {
15880 var number = number$1;
15881 var rgba = rgbaNoBackRefs;
15882 var hsla = hslaNoBackRefs;
15883 var hex3$1 = hex3;
15884 var hex6$1 = hex6;
15885
15886 var data = function data(prefix) {
15887 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
15888 };
15889
15890 var mapData = function mapData(prefix) {
15891 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
15892 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
15893 };
15894
15895 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
15896
15897 styfn$6.types = {
15898 time: {
15899 number: true,
15900 min: 0,
15901 units: 's|ms',
15902 implicitUnits: 'ms'
15903 },
15904 percent: {
15905 number: true,
15906 min: 0,
15907 max: 100,
15908 units: '%',
15909 implicitUnits: '%'
15910 },
15911 percentages: {
15912 number: true,
15913 min: 0,
15914 max: 100,
15915 units: '%',
15916 implicitUnits: '%',
15917 multiple: true
15918 },
15919 zeroOneNumber: {
15920 number: true,
15921 min: 0,
15922 max: 1,
15923 unitless: true
15924 },
15925 zeroOneNumbers: {
15926 number: true,
15927 min: 0,
15928 max: 1,
15929 unitless: true,
15930 multiple: true
15931 },
15932 nOneOneNumber: {
15933 number: true,
15934 min: -1,
15935 max: 1,
15936 unitless: true
15937 },
15938 nonNegativeInt: {
15939 number: true,
15940 min: 0,
15941 integer: true,
15942 unitless: true
15943 },
15944 position: {
15945 enums: ['parent', 'origin']
15946 },
15947 nodeSize: {
15948 number: true,
15949 min: 0,
15950 enums: ['label']
15951 },
15952 number: {
15953 number: true,
15954 unitless: true
15955 },
15956 numbers: {
15957 number: true,
15958 unitless: true,
15959 multiple: true
15960 },
15961 positiveNumber: {
15962 number: true,
15963 unitless: true,
15964 min: 0,
15965 strictMin: true
15966 },
15967 size: {
15968 number: true,
15969 min: 0
15970 },
15971 bidirectionalSize: {
15972 number: true
15973 },
15974 // allows negative
15975 bidirectionalSizeMaybePercent: {
15976 number: true,
15977 allowPercent: true
15978 },
15979 // allows negative
15980 bidirectionalSizes: {
15981 number: true,
15982 multiple: true
15983 },
15984 // allows negative
15985 sizeMaybePercent: {
15986 number: true,
15987 min: 0,
15988 allowPercent: true
15989 },
15990 axisDirection: {
15991 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
15992 },
15993 paddingRelativeTo: {
15994 enums: ['width', 'height', 'average', 'min', 'max']
15995 },
15996 bgWH: {
15997 number: true,
15998 min: 0,
15999 allowPercent: true,
16000 enums: ['auto'],
16001 multiple: true
16002 },
16003 bgPos: {
16004 number: true,
16005 allowPercent: true,
16006 multiple: true
16007 },
16008 bgRelativeTo: {
16009 enums: ['inner', 'include-padding'],
16010 multiple: true
16011 },
16012 bgRepeat: {
16013 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16014 multiple: true
16015 },
16016 bgFit: {
16017 enums: ['none', 'contain', 'cover'],
16018 multiple: true
16019 },
16020 bgCrossOrigin: {
16021 enums: ['anonymous', 'use-credentials'],
16022 multiple: true
16023 },
16024 bgClip: {
16025 enums: ['none', 'node'],
16026 multiple: true
16027 },
16028 bgContainment: {
16029 enums: ['inside', 'over'],
16030 multiple: true
16031 },
16032 color: {
16033 color: true
16034 },
16035 colors: {
16036 color: true,
16037 multiple: true
16038 },
16039 fill: {
16040 enums: ['solid', 'linear-gradient', 'radial-gradient']
16041 },
16042 bool: {
16043 enums: ['yes', 'no']
16044 },
16045 bools: {
16046 enums: ['yes', 'no'],
16047 multiple: true
16048 },
16049 lineStyle: {
16050 enums: ['solid', 'dotted', 'dashed']
16051 },
16052 lineCap: {
16053 enums: ['butt', 'round', 'square']
16054 },
16055 borderStyle: {
16056 enums: ['solid', 'dotted', 'dashed', 'double']
16057 },
16058 curveStyle: {
16059 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
16060 },
16061 fontFamily: {
16062 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16063 },
16064 fontStyle: {
16065 enums: ['italic', 'normal', 'oblique']
16066 },
16067 fontWeight: {
16068 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16069 },
16070 textDecoration: {
16071 enums: ['none', 'underline', 'overline', 'line-through']
16072 },
16073 textTransform: {
16074 enums: ['none', 'uppercase', 'lowercase']
16075 },
16076 textWrap: {
16077 enums: ['none', 'wrap', 'ellipsis']
16078 },
16079 textOverflowWrap: {
16080 enums: ['whitespace', 'anywhere']
16081 },
16082 textBackgroundShape: {
16083 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16084 },
16085 nodeShape: {
16086 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']
16087 },
16088 compoundIncludeLabels: {
16089 enums: ['include', 'exclude']
16090 },
16091 arrowShape: {
16092 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16093 },
16094 arrowFill: {
16095 enums: ['filled', 'hollow']
16096 },
16097 display: {
16098 enums: ['element', 'none']
16099 },
16100 visibility: {
16101 enums: ['hidden', 'visible']
16102 },
16103 zCompoundDepth: {
16104 enums: ['bottom', 'orphan', 'auto', 'top']
16105 },
16106 zIndexCompare: {
16107 enums: ['auto', 'manual']
16108 },
16109 valign: {
16110 enums: ['top', 'center', 'bottom']
16111 },
16112 halign: {
16113 enums: ['left', 'center', 'right']
16114 },
16115 justification: {
16116 enums: ['left', 'center', 'right', 'auto']
16117 },
16118 text: {
16119 string: true
16120 },
16121 data: {
16122 mapping: true,
16123 regex: data('data')
16124 },
16125 layoutData: {
16126 mapping: true,
16127 regex: data('layoutData')
16128 },
16129 scratch: {
16130 mapping: true,
16131 regex: data('scratch')
16132 },
16133 mapData: {
16134 mapping: true,
16135 regex: mapData('mapData')
16136 },
16137 mapLayoutData: {
16138 mapping: true,
16139 regex: mapData('mapLayoutData')
16140 },
16141 mapScratch: {
16142 mapping: true,
16143 regex: mapData('mapScratch')
16144 },
16145 fn: {
16146 mapping: true,
16147 fn: true
16148 },
16149 url: {
16150 regexes: urlRegexes,
16151 singleRegexMatchValue: true
16152 },
16153 urls: {
16154 regexes: urlRegexes,
16155 singleRegexMatchValue: true,
16156 multiple: true
16157 },
16158 propList: {
16159 propList: true
16160 },
16161 angle: {
16162 number: true,
16163 units: 'deg|rad',
16164 implicitUnits: 'rad'
16165 },
16166 textRotation: {
16167 number: true,
16168 units: 'deg|rad',
16169 implicitUnits: 'rad',
16170 enums: ['none', 'autorotate']
16171 },
16172 polygonPointList: {
16173 number: true,
16174 multiple: true,
16175 evenMultiple: true,
16176 min: -1,
16177 max: 1,
16178 unitless: true
16179 },
16180 edgeDistances: {
16181 enums: ['intersection', 'node-position']
16182 },
16183 edgeEndpoint: {
16184 number: true,
16185 multiple: true,
16186 units: '%|px|em|deg|rad',
16187 implicitUnits: 'px',
16188 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16189 singleEnum: true,
16190 validate: function validate(valArr, unitsArr) {
16191 switch (valArr.length) {
16192 case 2:
16193 // can be % or px only
16194 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16195
16196 case 1:
16197 // can be enum, deg, or rad only
16198 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16199
16200 default:
16201 return false;
16202 }
16203 }
16204 },
16205 easing: {
16206 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
16207 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']
16208 },
16209 gradientDirection: {
16210 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']
16211 },
16212 boundsExpansion: {
16213 number: true,
16214 multiple: true,
16215 min: 0,
16216 validate: function validate(valArr) {
16217 var length = valArr.length;
16218 return length === 1 || length === 2 || length === 4;
16219 }
16220 }
16221 };
16222 var diff = {
16223 zeroNonZero: function zeroNonZero(val1, val2) {
16224 if ((val1 == null || val2 == null) && val1 !== val2) {
16225 return true; // null cases could represent any value
16226 }
16227
16228 if (val1 == 0 && val2 != 0) {
16229 return true;
16230 } else if (val1 != 0 && val2 == 0) {
16231 return true;
16232 } else {
16233 return false;
16234 }
16235 },
16236 any: function any(val1, val2) {
16237 return val1 != val2;
16238 },
16239 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
16240 var empty1 = emptyString(str1);
16241 var empty2 = emptyString(str2);
16242 return empty1 && !empty2 || !empty1 && empty2;
16243 }
16244 }; // define visual style properties
16245 //
16246 // - n.b. adding a new group of props may require updates to updateStyleHints()
16247 // - adding new props to an existing group gets handled automatically
16248
16249 var t = styfn$6.types;
16250 var mainLabel = [{
16251 name: 'label',
16252 type: t.text,
16253 triggersBounds: diff.any,
16254 triggersZOrder: diff.emptyNonEmpty
16255 }, {
16256 name: 'text-rotation',
16257 type: t.textRotation,
16258 triggersBounds: diff.any
16259 }, {
16260 name: 'text-margin-x',
16261 type: t.bidirectionalSize,
16262 triggersBounds: diff.any
16263 }, {
16264 name: 'text-margin-y',
16265 type: t.bidirectionalSize,
16266 triggersBounds: diff.any
16267 }];
16268 var sourceLabel = [{
16269 name: 'source-label',
16270 type: t.text,
16271 triggersBounds: diff.any
16272 }, {
16273 name: 'source-text-rotation',
16274 type: t.textRotation,
16275 triggersBounds: diff.any
16276 }, {
16277 name: 'source-text-margin-x',
16278 type: t.bidirectionalSize,
16279 triggersBounds: diff.any
16280 }, {
16281 name: 'source-text-margin-y',
16282 type: t.bidirectionalSize,
16283 triggersBounds: diff.any
16284 }, {
16285 name: 'source-text-offset',
16286 type: t.size,
16287 triggersBounds: diff.any
16288 }];
16289 var targetLabel = [{
16290 name: 'target-label',
16291 type: t.text,
16292 triggersBounds: diff.any
16293 }, {
16294 name: 'target-text-rotation',
16295 type: t.textRotation,
16296 triggersBounds: diff.any
16297 }, {
16298 name: 'target-text-margin-x',
16299 type: t.bidirectionalSize,
16300 triggersBounds: diff.any
16301 }, {
16302 name: 'target-text-margin-y',
16303 type: t.bidirectionalSize,
16304 triggersBounds: diff.any
16305 }, {
16306 name: 'target-text-offset',
16307 type: t.size,
16308 triggersBounds: diff.any
16309 }];
16310 var labelDimensions = [{
16311 name: 'font-family',
16312 type: t.fontFamily,
16313 triggersBounds: diff.any
16314 }, {
16315 name: 'font-style',
16316 type: t.fontStyle,
16317 triggersBounds: diff.any
16318 }, {
16319 name: 'font-weight',
16320 type: t.fontWeight,
16321 triggersBounds: diff.any
16322 }, {
16323 name: 'font-size',
16324 type: t.size,
16325 triggersBounds: diff.any
16326 }, {
16327 name: 'text-transform',
16328 type: t.textTransform,
16329 triggersBounds: diff.any
16330 }, {
16331 name: 'text-wrap',
16332 type: t.textWrap,
16333 triggersBounds: diff.any
16334 }, {
16335 name: 'text-overflow-wrap',
16336 type: t.textOverflowWrap,
16337 triggersBounds: diff.any
16338 }, {
16339 name: 'text-max-width',
16340 type: t.size,
16341 triggersBounds: diff.any
16342 }, {
16343 name: 'text-outline-width',
16344 type: t.size,
16345 triggersBounds: diff.any
16346 }, {
16347 name: 'line-height',
16348 type: t.positiveNumber,
16349 triggersBounds: diff.any
16350 }];
16351 var commonLabel = [{
16352 name: 'text-valign',
16353 type: t.valign,
16354 triggersBounds: diff.any
16355 }, {
16356 name: 'text-halign',
16357 type: t.halign,
16358 triggersBounds: diff.any
16359 }, {
16360 name: 'color',
16361 type: t.color
16362 }, {
16363 name: 'text-outline-color',
16364 type: t.color
16365 }, {
16366 name: 'text-outline-opacity',
16367 type: t.zeroOneNumber
16368 }, {
16369 name: 'text-background-color',
16370 type: t.color
16371 }, {
16372 name: 'text-background-opacity',
16373 type: t.zeroOneNumber
16374 }, {
16375 name: 'text-background-padding',
16376 type: t.size,
16377 triggersBounds: diff.any
16378 }, {
16379 name: 'text-border-opacity',
16380 type: t.zeroOneNumber
16381 }, {
16382 name: 'text-border-color',
16383 type: t.color
16384 }, {
16385 name: 'text-border-width',
16386 type: t.size,
16387 triggersBounds: diff.any
16388 }, {
16389 name: 'text-border-style',
16390 type: t.borderStyle,
16391 triggersBounds: diff.any
16392 }, {
16393 name: 'text-background-shape',
16394 type: t.textBackgroundShape,
16395 triggersBounds: diff.any
16396 }, {
16397 name: 'text-justification',
16398 type: t.justification
16399 }];
16400 var behavior = [{
16401 name: 'events',
16402 type: t.bool
16403 }, {
16404 name: 'text-events',
16405 type: t.bool
16406 }];
16407 var visibility = [{
16408 name: 'display',
16409 type: t.display,
16410 triggersZOrder: diff.any,
16411 triggersBounds: diff.any,
16412 triggersBoundsOfParallelBeziers: true
16413 }, {
16414 name: 'visibility',
16415 type: t.visibility,
16416 triggersZOrder: diff.any
16417 }, {
16418 name: 'opacity',
16419 type: t.zeroOneNumber,
16420 triggersZOrder: diff.zeroNonZero
16421 }, {
16422 name: 'text-opacity',
16423 type: t.zeroOneNumber
16424 }, {
16425 name: 'min-zoomed-font-size',
16426 type: t.size
16427 }, {
16428 name: 'z-compound-depth',
16429 type: t.zCompoundDepth,
16430 triggersZOrder: diff.any
16431 }, {
16432 name: 'z-index-compare',
16433 type: t.zIndexCompare,
16434 triggersZOrder: diff.any
16435 }, {
16436 name: 'z-index',
16437 type: t.nonNegativeInt,
16438 triggersZOrder: diff.any
16439 }];
16440 var overlay = [{
16441 name: 'overlay-padding',
16442 type: t.size,
16443 triggersBounds: diff.any
16444 }, {
16445 name: 'overlay-color',
16446 type: t.color
16447 }, {
16448 name: 'overlay-opacity',
16449 type: t.zeroOneNumber,
16450 triggersBounds: diff.zeroNonZero
16451 }];
16452 var transition = [{
16453 name: 'transition-property',
16454 type: t.propList
16455 }, {
16456 name: 'transition-duration',
16457 type: t.time
16458 }, {
16459 name: 'transition-delay',
16460 type: t.time
16461 }, {
16462 name: 'transition-timing-function',
16463 type: t.easing
16464 }];
16465
16466 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16467 if (parsedProp.value === 'label') {
16468 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16469 } else {
16470 return parsedProp.pfValue;
16471 }
16472 };
16473
16474 var nodeBody = [{
16475 name: 'height',
16476 type: t.nodeSize,
16477 triggersBounds: diff.any,
16478 hashOverride: nodeSizeHashOverride
16479 }, {
16480 name: 'width',
16481 type: t.nodeSize,
16482 triggersBounds: diff.any,
16483 hashOverride: nodeSizeHashOverride
16484 }, {
16485 name: 'shape',
16486 type: t.nodeShape,
16487 triggersBounds: diff.any
16488 }, {
16489 name: 'shape-polygon-points',
16490 type: t.polygonPointList,
16491 triggersBounds: diff.any
16492 }, {
16493 name: 'background-color',
16494 type: t.color
16495 }, {
16496 name: 'background-fill',
16497 type: t.fill
16498 }, {
16499 name: 'background-opacity',
16500 type: t.zeroOneNumber
16501 }, {
16502 name: 'background-blacken',
16503 type: t.nOneOneNumber
16504 }, {
16505 name: 'background-gradient-stop-colors',
16506 type: t.colors
16507 }, {
16508 name: 'background-gradient-stop-positions',
16509 type: t.percentages
16510 }, {
16511 name: 'background-gradient-direction',
16512 type: t.gradientDirection
16513 }, {
16514 name: 'padding',
16515 type: t.sizeMaybePercent,
16516 triggersBounds: diff.any
16517 }, {
16518 name: 'padding-relative-to',
16519 type: t.paddingRelativeTo,
16520 triggersBounds: diff.any
16521 }, {
16522 name: 'bounds-expansion',
16523 type: t.boundsExpansion,
16524 triggersBounds: diff.any
16525 }];
16526 var nodeBorder = [{
16527 name: 'border-color',
16528 type: t.color
16529 }, {
16530 name: 'border-opacity',
16531 type: t.zeroOneNumber
16532 }, {
16533 name: 'border-width',
16534 type: t.size,
16535 triggersBounds: diff.any
16536 }, {
16537 name: 'border-style',
16538 type: t.borderStyle
16539 }];
16540 var backgroundImage = [{
16541 name: 'background-image',
16542 type: t.urls
16543 }, {
16544 name: 'background-image-crossorigin',
16545 type: t.bgCrossOrigin
16546 }, {
16547 name: 'background-image-opacity',
16548 type: t.zeroOneNumbers
16549 }, {
16550 name: 'background-image-containment',
16551 type: t.bgContainment
16552 }, {
16553 name: 'background-image-smoothing',
16554 type: t.bools
16555 }, {
16556 name: 'background-position-x',
16557 type: t.bgPos
16558 }, {
16559 name: 'background-position-y',
16560 type: t.bgPos
16561 }, {
16562 name: 'background-width-relative-to',
16563 type: t.bgRelativeTo
16564 }, {
16565 name: 'background-height-relative-to',
16566 type: t.bgRelativeTo
16567 }, {
16568 name: 'background-repeat',
16569 type: t.bgRepeat
16570 }, {
16571 name: 'background-fit',
16572 type: t.bgFit
16573 }, {
16574 name: 'background-clip',
16575 type: t.bgClip
16576 }, {
16577 name: 'background-width',
16578 type: t.bgWH
16579 }, {
16580 name: 'background-height',
16581 type: t.bgWH
16582 }, {
16583 name: 'background-offset-x',
16584 type: t.bgPos
16585 }, {
16586 name: 'background-offset-y',
16587 type: t.bgPos
16588 }];
16589 var compound = [{
16590 name: 'position',
16591 type: t.position,
16592 triggersBounds: diff.any
16593 }, {
16594 name: 'compound-sizing-wrt-labels',
16595 type: t.compoundIncludeLabels,
16596 triggersBounds: diff.any
16597 }, {
16598 name: 'min-width',
16599 type: t.size,
16600 triggersBounds: diff.any
16601 }, {
16602 name: 'min-width-bias-left',
16603 type: t.sizeMaybePercent,
16604 triggersBounds: diff.any
16605 }, {
16606 name: 'min-width-bias-right',
16607 type: t.sizeMaybePercent,
16608 triggersBounds: diff.any
16609 }, {
16610 name: 'min-height',
16611 type: t.size,
16612 triggersBounds: diff.any
16613 }, {
16614 name: 'min-height-bias-top',
16615 type: t.sizeMaybePercent,
16616 triggersBounds: diff.any
16617 }, {
16618 name: 'min-height-bias-bottom',
16619 type: t.sizeMaybePercent,
16620 triggersBounds: diff.any
16621 }];
16622 var edgeLine = [{
16623 name: 'line-style',
16624 type: t.lineStyle
16625 }, {
16626 name: 'line-color',
16627 type: t.color
16628 }, {
16629 name: 'line-fill',
16630 type: t.fill
16631 }, {
16632 name: 'line-cap',
16633 type: t.lineCap
16634 }, {
16635 name: 'line-opacity',
16636 type: t.zeroOneNumber
16637 }, {
16638 name: 'line-dash-pattern',
16639 type: t.numbers
16640 }, {
16641 name: 'line-dash-offset',
16642 type: t.number
16643 }, {
16644 name: 'line-gradient-stop-colors',
16645 type: t.colors
16646 }, {
16647 name: 'line-gradient-stop-positions',
16648 type: t.percentages
16649 }, {
16650 name: 'curve-style',
16651 type: t.curveStyle,
16652 triggersBounds: diff.any,
16653 triggersBoundsOfParallelBeziers: true
16654 }, {
16655 name: 'haystack-radius',
16656 type: t.zeroOneNumber,
16657 triggersBounds: diff.any
16658 }, {
16659 name: 'source-endpoint',
16660 type: t.edgeEndpoint,
16661 triggersBounds: diff.any
16662 }, {
16663 name: 'target-endpoint',
16664 type: t.edgeEndpoint,
16665 triggersBounds: diff.any
16666 }, {
16667 name: 'control-point-step-size',
16668 type: t.size,
16669 triggersBounds: diff.any
16670 }, {
16671 name: 'control-point-distances',
16672 type: t.bidirectionalSizes,
16673 triggersBounds: diff.any
16674 }, {
16675 name: 'control-point-weights',
16676 type: t.numbers,
16677 triggersBounds: diff.any
16678 }, {
16679 name: 'segment-distances',
16680 type: t.bidirectionalSizes,
16681 triggersBounds: diff.any
16682 }, {
16683 name: 'segment-weights',
16684 type: t.numbers,
16685 triggersBounds: diff.any
16686 }, {
16687 name: 'taxi-turn',
16688 type: t.bidirectionalSizeMaybePercent,
16689 triggersBounds: diff.any
16690 }, {
16691 name: 'taxi-turn-min-distance',
16692 type: t.size,
16693 triggersBounds: diff.any
16694 }, {
16695 name: 'taxi-direction',
16696 type: t.axisDirection,
16697 triggersBounds: diff.any
16698 }, {
16699 name: 'edge-distances',
16700 type: t.edgeDistances,
16701 triggersBounds: diff.any
16702 }, {
16703 name: 'arrow-scale',
16704 type: t.positiveNumber,
16705 triggersBounds: diff.any
16706 }, {
16707 name: 'loop-direction',
16708 type: t.angle,
16709 triggersBounds: diff.any
16710 }, {
16711 name: 'loop-sweep',
16712 type: t.angle,
16713 triggersBounds: diff.any
16714 }, {
16715 name: 'source-distance-from-node',
16716 type: t.size,
16717 triggersBounds: diff.any
16718 }, {
16719 name: 'target-distance-from-node',
16720 type: t.size,
16721 triggersBounds: diff.any
16722 }];
16723 var ghost = [{
16724 name: 'ghost',
16725 type: t.bool,
16726 triggersBounds: diff.any
16727 }, {
16728 name: 'ghost-offset-x',
16729 type: t.bidirectionalSize,
16730 triggersBounds: diff.any
16731 }, {
16732 name: 'ghost-offset-y',
16733 type: t.bidirectionalSize,
16734 triggersBounds: diff.any
16735 }, {
16736 name: 'ghost-opacity',
16737 type: t.zeroOneNumber
16738 }];
16739 var core = [{
16740 name: 'selection-box-color',
16741 type: t.color
16742 }, {
16743 name: 'selection-box-opacity',
16744 type: t.zeroOneNumber
16745 }, {
16746 name: 'selection-box-border-color',
16747 type: t.color
16748 }, {
16749 name: 'selection-box-border-width',
16750 type: t.size
16751 }, {
16752 name: 'active-bg-color',
16753 type: t.color
16754 }, {
16755 name: 'active-bg-opacity',
16756 type: t.zeroOneNumber
16757 }, {
16758 name: 'active-bg-size',
16759 type: t.size
16760 }, {
16761 name: 'outside-texture-bg-color',
16762 type: t.color
16763 }, {
16764 name: 'outside-texture-bg-opacity',
16765 type: t.zeroOneNumber
16766 }]; // pie backgrounds for nodes
16767
16768 var pie = [];
16769 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
16770
16771 pie.push({
16772 name: 'pie-size',
16773 type: t.sizeMaybePercent
16774 });
16775
16776 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
16777 pie.push({
16778 name: 'pie-' + i + '-background-color',
16779 type: t.color
16780 });
16781 pie.push({
16782 name: 'pie-' + i + '-background-size',
16783 type: t.percent
16784 });
16785 pie.push({
16786 name: 'pie-' + i + '-background-opacity',
16787 type: t.zeroOneNumber
16788 });
16789 } // edge arrows
16790
16791
16792 var edgeArrow = [];
16793 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
16794 [{
16795 name: 'arrow-shape',
16796 type: t.arrowShape,
16797 triggersBounds: diff.any
16798 }, {
16799 name: 'arrow-color',
16800 type: t.color
16801 }, {
16802 name: 'arrow-fill',
16803 type: t.arrowFill
16804 }].forEach(function (prop) {
16805 arrowPrefixes.forEach(function (prefix) {
16806 var name = prefix + '-' + prop.name;
16807 var type = prop.type,
16808 triggersBounds = prop.triggersBounds;
16809 edgeArrow.push({
16810 name: name,
16811 type: type,
16812 triggersBounds: triggersBounds
16813 });
16814 });
16815 }, {});
16816 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
16817 var propGroups = styfn$6.propertyGroups = {
16818 // common to all eles
16819 behavior: behavior,
16820 transition: transition,
16821 visibility: visibility,
16822 overlay: overlay,
16823 ghost: ghost,
16824 // labels
16825 commonLabel: commonLabel,
16826 labelDimensions: labelDimensions,
16827 mainLabel: mainLabel,
16828 sourceLabel: sourceLabel,
16829 targetLabel: targetLabel,
16830 // node props
16831 nodeBody: nodeBody,
16832 nodeBorder: nodeBorder,
16833 backgroundImage: backgroundImage,
16834 pie: pie,
16835 compound: compound,
16836 // edge props
16837 edgeLine: edgeLine,
16838 edgeArrow: edgeArrow,
16839 core: core
16840 };
16841 var propGroupNames = styfn$6.propertyGroupNames = {};
16842 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
16843 propGroupKeys.forEach(function (key) {
16844 propGroupNames[key] = propGroups[key].map(function (prop) {
16845 return prop.name;
16846 });
16847 propGroups[key].forEach(function (prop) {
16848 return prop.groupKey = key;
16849 });
16850 }); // define aliases
16851
16852 var aliases = styfn$6.aliases = [{
16853 name: 'content',
16854 pointsTo: 'label'
16855 }, {
16856 name: 'control-point-distance',
16857 pointsTo: 'control-point-distances'
16858 }, {
16859 name: 'control-point-weight',
16860 pointsTo: 'control-point-weights'
16861 }, {
16862 name: 'edge-text-rotation',
16863 pointsTo: 'text-rotation'
16864 }, {
16865 name: 'padding-left',
16866 pointsTo: 'padding'
16867 }, {
16868 name: 'padding-right',
16869 pointsTo: 'padding'
16870 }, {
16871 name: 'padding-top',
16872 pointsTo: 'padding'
16873 }, {
16874 name: 'padding-bottom',
16875 pointsTo: 'padding'
16876 }]; // list of property names
16877
16878 styfn$6.propertyNames = props.map(function (p) {
16879 return p.name;
16880 }); // allow access of properties by name ( e.g. style.properties.height )
16881
16882 for (var _i = 0; _i < props.length; _i++) {
16883 var prop = props[_i];
16884 props[prop.name] = prop; // allow lookup by name
16885 } // map aliases
16886
16887
16888 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
16889 var alias = aliases[_i2];
16890 var pointsToProp = props[alias.pointsTo];
16891 var aliasProp = {
16892 name: alias.name,
16893 alias: true,
16894 pointsTo: pointsToProp
16895 }; // add alias prop for parsing
16896
16897 props.push(aliasProp);
16898 props[alias.name] = aliasProp; // allow lookup by name
16899 }
16900})();
16901
16902styfn$6.getDefaultProperty = function (name) {
16903 return this.getDefaultProperties()[name];
16904};
16905
16906styfn$6.getDefaultProperties = function () {
16907 var _p = this._private;
16908
16909 if (_p.defaultProperties != null) {
16910 return _p.defaultProperties;
16911 }
16912
16913 var rawProps = extend({
16914 // core props
16915 'selection-box-color': '#ddd',
16916 'selection-box-opacity': 0.65,
16917 'selection-box-border-color': '#aaa',
16918 'selection-box-border-width': 1,
16919 'active-bg-color': 'black',
16920 'active-bg-opacity': 0.15,
16921 'active-bg-size': 30,
16922 'outside-texture-bg-color': '#000',
16923 'outside-texture-bg-opacity': 0.125,
16924 // common node/edge props
16925 'events': 'yes',
16926 'text-events': 'no',
16927 'text-valign': 'top',
16928 'text-halign': 'center',
16929 'text-justification': 'auto',
16930 'line-height': 1,
16931 'color': '#000',
16932 'text-outline-color': '#000',
16933 'text-outline-width': 0,
16934 'text-outline-opacity': 1,
16935 'text-opacity': 1,
16936 'text-decoration': 'none',
16937 'text-transform': 'none',
16938 'text-wrap': 'none',
16939 'text-overflow-wrap': 'whitespace',
16940 'text-max-width': 9999,
16941 'text-background-color': '#000',
16942 'text-background-opacity': 0,
16943 'text-background-shape': 'rectangle',
16944 'text-background-padding': 0,
16945 'text-border-opacity': 0,
16946 'text-border-width': 0,
16947 'text-border-style': 'solid',
16948 'text-border-color': '#000',
16949 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
16950 'font-style': 'normal',
16951 'font-weight': 'normal',
16952 'font-size': 16,
16953 'min-zoomed-font-size': 0,
16954 'text-rotation': 'none',
16955 'source-text-rotation': 'none',
16956 'target-text-rotation': 'none',
16957 'visibility': 'visible',
16958 'display': 'element',
16959 'opacity': 1,
16960 'z-compound-depth': 'auto',
16961 'z-index-compare': 'auto',
16962 'z-index': 0,
16963 'label': '',
16964 'text-margin-x': 0,
16965 'text-margin-y': 0,
16966 'source-label': '',
16967 'source-text-offset': 0,
16968 'source-text-margin-x': 0,
16969 'source-text-margin-y': 0,
16970 'target-label': '',
16971 'target-text-offset': 0,
16972 'target-text-margin-x': 0,
16973 'target-text-margin-y': 0,
16974 'overlay-opacity': 0,
16975 'overlay-color': '#000',
16976 'overlay-padding': 10,
16977 'transition-property': 'none',
16978 'transition-duration': 0,
16979 'transition-delay': 0,
16980 'transition-timing-function': 'linear',
16981 // node props
16982 'background-blacken': 0,
16983 'background-color': '#999',
16984 'background-fill': 'solid',
16985 'background-opacity': 1,
16986 'background-image': 'none',
16987 'background-image-crossorigin': 'anonymous',
16988 'background-image-opacity': 1,
16989 'background-image-containment': 'inside',
16990 'background-image-smoothing': 'yes',
16991 'background-position-x': '50%',
16992 'background-position-y': '50%',
16993 'background-offset-x': 0,
16994 'background-offset-y': 0,
16995 'background-width-relative-to': 'include-padding',
16996 'background-height-relative-to': 'include-padding',
16997 'background-repeat': 'no-repeat',
16998 'background-fit': 'none',
16999 'background-clip': 'node',
17000 'background-width': 'auto',
17001 'background-height': 'auto',
17002 'border-color': '#000',
17003 'border-opacity': 1,
17004 'border-width': 0,
17005 'border-style': 'solid',
17006 'height': 30,
17007 'width': 30,
17008 'shape': 'ellipse',
17009 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17010 'bounds-expansion': 0,
17011 // node gradient
17012 'background-gradient-direction': 'to-bottom',
17013 'background-gradient-stop-colors': '#999',
17014 'background-gradient-stop-positions': '0%',
17015 // ghost props
17016 'ghost': 'no',
17017 'ghost-offset-y': 0,
17018 'ghost-offset-x': 0,
17019 'ghost-opacity': 0,
17020 // compound props
17021 'padding': 0,
17022 'padding-relative-to': 'width',
17023 'position': 'origin',
17024 'compound-sizing-wrt-labels': 'include',
17025 'min-width': 0,
17026 'min-width-bias-left': 0,
17027 'min-width-bias-right': 0,
17028 'min-height': 0,
17029 'min-height-bias-top': 0,
17030 'min-height-bias-bottom': 0
17031 }, {
17032 // node pie bg
17033 'pie-size': '100%'
17034 }, [{
17035 name: 'pie-{{i}}-background-color',
17036 value: 'black'
17037 }, {
17038 name: 'pie-{{i}}-background-size',
17039 value: '0%'
17040 }, {
17041 name: 'pie-{{i}}-background-opacity',
17042 value: 1
17043 }].reduce(function (css, prop) {
17044 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17045 var name = prop.name.replace('{{i}}', i);
17046 var val = prop.value;
17047 css[name] = val;
17048 }
17049
17050 return css;
17051 }, {}), {
17052 // edge props
17053 'line-style': 'solid',
17054 'line-color': '#999',
17055 'line-fill': 'solid',
17056 'line-cap': 'butt',
17057 'line-opacity': 1,
17058 'line-gradient-stop-colors': '#999',
17059 'line-gradient-stop-positions': '0%',
17060 'control-point-step-size': 40,
17061 'control-point-weights': 0.5,
17062 'segment-weights': 0.5,
17063 'segment-distances': 20,
17064 'taxi-turn': '50%',
17065 'taxi-turn-min-distance': 10,
17066 'taxi-direction': 'auto',
17067 'edge-distances': 'intersection',
17068 'curve-style': 'haystack',
17069 'haystack-radius': 0,
17070 'arrow-scale': 1,
17071 'loop-direction': '-45deg',
17072 'loop-sweep': '-90deg',
17073 'source-distance-from-node': 0,
17074 'target-distance-from-node': 0,
17075 'source-endpoint': 'outside-to-node',
17076 'target-endpoint': 'outside-to-node',
17077 'line-dash-pattern': [6, 3],
17078 'line-dash-offset': 0
17079 }, [{
17080 name: 'arrow-shape',
17081 value: 'none'
17082 }, {
17083 name: 'arrow-color',
17084 value: '#999'
17085 }, {
17086 name: 'arrow-fill',
17087 value: 'filled'
17088 }].reduce(function (css, prop) {
17089 styfn$6.arrowPrefixes.forEach(function (prefix) {
17090 var name = prefix + '-' + prop.name;
17091 var val = prop.value;
17092 css[name] = val;
17093 });
17094 return css;
17095 }, {}));
17096 var parsedProps = {};
17097
17098 for (var i = 0; i < this.properties.length; i++) {
17099 var prop = this.properties[i];
17100
17101 if (prop.pointsTo) {
17102 continue;
17103 }
17104
17105 var name = prop.name;
17106 var val = rawProps[name];
17107 var parsedProp = this.parse(name, val);
17108 parsedProps[name] = parsedProp;
17109 }
17110
17111 _p.defaultProperties = parsedProps;
17112 return _p.defaultProperties;
17113};
17114
17115styfn$6.addDefaultStylesheet = function () {
17116 this.selector(':parent').css({
17117 'shape': 'rectangle',
17118 'padding': 10,
17119 'background-color': '#eee',
17120 'border-color': '#ccc',
17121 'border-width': 1
17122 }).selector('edge').css({
17123 'width': 3
17124 }).selector(':loop').css({
17125 'curve-style': 'bezier'
17126 }).selector('edge:compound').css({
17127 'curve-style': 'bezier',
17128 'source-endpoint': 'outside-to-line',
17129 'target-endpoint': 'outside-to-line'
17130 }).selector(':selected').css({
17131 'background-color': '#0169D9',
17132 'line-color': '#0169D9',
17133 'source-arrow-color': '#0169D9',
17134 'target-arrow-color': '#0169D9',
17135 'mid-source-arrow-color': '#0169D9',
17136 'mid-target-arrow-color': '#0169D9'
17137 }).selector(':parent:selected').css({
17138 'background-color': '#CCE1F9',
17139 'border-color': '#aec8e5'
17140 }).selector(':active').css({
17141 'overlay-color': 'black',
17142 'overlay-padding': 10,
17143 'overlay-opacity': 0.25
17144 });
17145 this.defaultLength = this.length;
17146};
17147
17148var styfn$7 = {}; // a caching layer for property parsing
17149
17150styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
17151 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17152
17153 if (fn(value)) {
17154 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17155 }
17156
17157 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17158 var bypassKey = propIsBypass ? 't' : 'f';
17159 var valueKey = '' + value;
17160 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17161 var propCache = self.propCache = self.propCache || [];
17162 var ret;
17163
17164 if (!(ret = propCache[argHash])) {
17165 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17166 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17167 // - mappings can't be shared b/c mappings are per-element
17168
17169
17170 if (propIsBypass || propIsFlat === 'mapping') {
17171 // need a copy since props are mutated later in their lifecycles
17172 ret = copy(ret);
17173
17174 if (ret) {
17175 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17176 }
17177 }
17178
17179 return ret;
17180};
17181
17182styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17183 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17184
17185 if (!prop && value != null) {
17186 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17187 }
17188
17189 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17190 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17191 }
17192
17193 return prop;
17194}; // parse a property; return null on invalid; return parsed property otherwise
17195// fields :
17196// - name : the name of the property
17197// - value : the parsed, native-typed value of the property
17198// - strValue : a string value that represents the property value in valid css
17199// - bypass : true iff the property is a bypass property
17200
17201
17202styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17203 var self = this;
17204 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17205
17206 var property = self.properties[name];
17207 var passedValue = value;
17208 var types = self.types;
17209
17210 if (!property) {
17211 return null;
17212 } // return null on property of unknown name
17213
17214
17215 if (value === undefined) {
17216 return null;
17217 } // can't assign undefined
17218 // the property may be an alias
17219
17220
17221 if (property.alias) {
17222 property = property.pointsTo;
17223 name = property.name;
17224 }
17225
17226 var valueIsString = string(value);
17227
17228 if (valueIsString) {
17229 // trim the value to make parsing easier
17230 value = value.trim();
17231 }
17232
17233 var type = property.type;
17234
17235 if (!type) {
17236 return null;
17237 } // no type, no luck
17238 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17239
17240
17241 if (propIsBypass && (value === '' || value === null)) {
17242 return {
17243 name: name,
17244 value: value,
17245 bypass: true,
17246 deleteBypass: true
17247 };
17248 } // check if value is a function used as a mapper
17249
17250
17251 if (fn(value)) {
17252 return {
17253 name: name,
17254 value: value,
17255 strValue: 'fn',
17256 mapped: types.fn,
17257 bypass: propIsBypass
17258 };
17259 } // check if value is mapped
17260
17261
17262 var data, mapData;
17263
17264 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))) {
17265 if (propIsBypass) {
17266 return false;
17267 } // mappers not allowed in bypass
17268
17269
17270 var mapped = types.data;
17271 return {
17272 name: name,
17273 value: data,
17274 strValue: '' + value,
17275 mapped: mapped,
17276 field: data[1],
17277 bypass: propIsBypass
17278 };
17279 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17280 if (propIsBypass) {
17281 return false;
17282 } // mappers not allowed in bypass
17283
17284
17285 if (type.multiple) {
17286 return false;
17287 } // impossible to map to num
17288
17289
17290 var _mapped = types.mapData; // we can map only if the type is a colour or a number
17291
17292 if (!(type.color || type.number)) {
17293 return false;
17294 }
17295
17296 var valueMin = this.parse(name, mapData[4]); // parse to validate
17297
17298 if (!valueMin || valueMin.mapped) {
17299 return false;
17300 } // can't be invalid or mapped
17301
17302
17303 var valueMax = this.parse(name, mapData[5]); // parse to validate
17304
17305 if (!valueMax || valueMax.mapped) {
17306 return false;
17307 } // can't be invalid or mapped
17308 // check if valueMin and valueMax are the same
17309
17310
17311 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17312 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17313 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17314 } else if (type.color) {
17315 var c1 = valueMin.value;
17316 var c2 = valueMax.value;
17317 var same = c1[0] === c2[0] // red
17318 && c1[1] === c2[1] // green
17319 && c1[2] === c2[2] // blue
17320 && ( // optional alpha
17321 c1[3] === c2[3] // same alpha outright
17322 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
17323 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17324 );
17325
17326 if (same) {
17327 return false;
17328 } // can't make a mapper without a range
17329
17330 }
17331
17332 return {
17333 name: name,
17334 value: mapData,
17335 strValue: '' + value,
17336 mapped: _mapped,
17337 field: mapData[1],
17338 fieldMin: parseFloat(mapData[2]),
17339 // min & max are numeric
17340 fieldMax: parseFloat(mapData[3]),
17341 valueMin: valueMin.value,
17342 valueMax: valueMax.value,
17343 bypass: propIsBypass
17344 };
17345 }
17346
17347 if (type.multiple && propIsFlat !== 'multiple') {
17348 var vals;
17349
17350 if (valueIsString) {
17351 vals = value.split(/\s+/);
17352 } else if (array(value)) {
17353 vals = value;
17354 } else {
17355 vals = [value];
17356 }
17357
17358 if (type.evenMultiple && vals.length % 2 !== 0) {
17359 return null;
17360 }
17361
17362 var valArr = [];
17363 var unitsArr = [];
17364 var pfValArr = [];
17365 var strVal = '';
17366 var hasEnum = false;
17367
17368 for (var i = 0; i < vals.length; i++) {
17369 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17370 hasEnum = hasEnum || string(p.value);
17371 valArr.push(p.value);
17372 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17373 unitsArr.push(p.units);
17374 strVal += (i > 0 ? ' ' : '') + p.strValue;
17375 }
17376
17377 if (type.validate && !type.validate(valArr, unitsArr)) {
17378 return null;
17379 }
17380
17381 if (type.singleEnum && hasEnum) {
17382 if (valArr.length === 1 && string(valArr[0])) {
17383 return {
17384 name: name,
17385 value: valArr[0],
17386 strValue: valArr[0],
17387 bypass: propIsBypass
17388 };
17389 } else {
17390 return null;
17391 }
17392 }
17393
17394 return {
17395 name: name,
17396 value: valArr,
17397 pfValue: pfValArr,
17398 strValue: strVal,
17399 bypass: propIsBypass,
17400 units: unitsArr
17401 };
17402 } // several types also allow enums
17403
17404
17405 var checkEnums = function checkEnums() {
17406 for (var _i = 0; _i < type.enums.length; _i++) {
17407 var en = type.enums[_i];
17408
17409 if (en === value) {
17410 return {
17411 name: name,
17412 value: value,
17413 strValue: '' + value,
17414 bypass: propIsBypass
17415 };
17416 }
17417 }
17418
17419 return null;
17420 }; // check the type and return the appropriate object
17421
17422
17423 if (type.number) {
17424 var units;
17425 var implicitUnits = 'px'; // not set => px
17426
17427 if (type.units) {
17428 // use specified units if set
17429 units = type.units;
17430 }
17431
17432 if (type.implicitUnits) {
17433 implicitUnits = type.implicitUnits;
17434 }
17435
17436 if (!type.unitless) {
17437 if (valueIsString) {
17438 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17439
17440 if (units) {
17441 unitsRegex = units;
17442 } // only allow explicit units if so set
17443
17444
17445 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
17446
17447 if (match) {
17448 value = match[1];
17449 units = match[2] || implicitUnits;
17450 }
17451 } else if (!units || type.implicitUnits) {
17452 units = implicitUnits; // implicitly px if unspecified
17453 }
17454 }
17455
17456 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
17457
17458 if (isNaN(value) && type.enums === undefined) {
17459 return null;
17460 } // check if this number type also accepts special keywords in place of numbers
17461 // (i.e. `left`, `auto`, etc)
17462
17463
17464 if (isNaN(value) && type.enums !== undefined) {
17465 value = passedValue;
17466 return checkEnums();
17467 } // check if value must be an integer
17468
17469
17470 if (type.integer && !integer(value)) {
17471 return null;
17472 } // check value is within range
17473
17474
17475 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17476 return null;
17477 }
17478
17479 var ret = {
17480 name: name,
17481 value: value,
17482 strValue: '' + value + (units ? units : ''),
17483 units: units,
17484 bypass: propIsBypass
17485 }; // normalise value in pixels
17486
17487 if (type.unitless || units !== 'px' && units !== 'em') {
17488 ret.pfValue = value;
17489 } else {
17490 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17491 } // normalise value in ms
17492
17493
17494 if (units === 'ms' || units === 's') {
17495 ret.pfValue = units === 'ms' ? value : 1000 * value;
17496 } // normalise value in rad
17497
17498
17499 if (units === 'deg' || units === 'rad') {
17500 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17501 } // normalize value in %
17502
17503
17504 if (units === '%') {
17505 ret.pfValue = value / 100;
17506 }
17507
17508 return ret;
17509 } else if (type.propList) {
17510 var props = [];
17511 var propsStr = '' + value;
17512
17513 if (propsStr === 'none') ; else {
17514 // go over each prop
17515 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17516
17517 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17518 var propName = propsSplit[_i2].trim();
17519
17520 if (self.properties[propName]) {
17521 props.push(propName);
17522 } else {
17523 warn('`' + propName + '` is not a valid property name');
17524 }
17525 }
17526
17527 if (props.length === 0) {
17528 return null;
17529 }
17530 }
17531
17532 return {
17533 name: name,
17534 value: props,
17535 strValue: props.length === 0 ? 'none' : props.join(' '),
17536 bypass: propIsBypass
17537 };
17538 } else if (type.color) {
17539 var tuple = color2tuple(value);
17540
17541 if (!tuple) {
17542 return null;
17543 }
17544
17545 return {
17546 name: name,
17547 value: tuple,
17548 pfValue: tuple,
17549 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
17550 // n.b. no spaces b/c of multiple support
17551 bypass: propIsBypass
17552 };
17553 } else if (type.regex || type.regexes) {
17554 // first check enums
17555 if (type.enums) {
17556 var enumProp = checkEnums();
17557
17558 if (enumProp) {
17559 return enumProp;
17560 }
17561 }
17562
17563 var regexes = type.regexes ? type.regexes : [type.regex];
17564
17565 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
17566 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
17567
17568 var m = regex.exec(value);
17569
17570 if (m) {
17571 // regex matches
17572 return {
17573 name: name,
17574 value: type.singleRegexMatchValue ? m[1] : m,
17575 strValue: '' + value,
17576 bypass: propIsBypass
17577 };
17578 }
17579 }
17580
17581 return null; // didn't match any
17582 } else if (type.string) {
17583 // just return
17584 return {
17585 name: name,
17586 value: '' + value,
17587 strValue: '' + value,
17588 bypass: propIsBypass
17589 };
17590 } else if (type.enums) {
17591 // check enums last because it's a combo type in others
17592 return checkEnums();
17593 } else {
17594 return null; // not a type we can handle
17595 }
17596};
17597
17598var Style = function Style(cy) {
17599 if (!(this instanceof Style)) {
17600 return new Style(cy);
17601 }
17602
17603 if (!core(cy)) {
17604 error('A style must have a core reference');
17605 return;
17606 }
17607
17608 this._private = {
17609 cy: cy,
17610 coreStyle: {}
17611 };
17612 this.length = 0;
17613 this.resetToDefault();
17614};
17615
17616var styfn$8 = Style.prototype;
17617
17618styfn$8.instanceString = function () {
17619 return 'style';
17620}; // remove all contexts
17621
17622
17623styfn$8.clear = function () {
17624 var _p = this._private;
17625 var cy = _p.cy;
17626 var eles = cy.elements();
17627
17628 for (var i = 0; i < this.length; i++) {
17629 this[i] = undefined;
17630 }
17631
17632 this.length = 0;
17633 _p.contextStyles = {};
17634 _p.propDiffs = {};
17635 this.cleanElements(eles, true);
17636 eles.forEach(function (ele) {
17637 var ele_p = ele[0]._private;
17638 ele_p.styleDirty = true;
17639 ele_p.appliedInitStyle = false;
17640 });
17641 return this; // chaining
17642};
17643
17644styfn$8.resetToDefault = function () {
17645 this.clear();
17646 this.addDefaultStylesheet();
17647 return this;
17648}; // builds a style object for the 'core' selector
17649
17650
17651styfn$8.core = function (propName) {
17652 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
17653}; // create a new context from the specified selector string and switch to that context
17654
17655
17656styfn$8.selector = function (selectorStr) {
17657 // 'core' is a special case and does not need a selector
17658 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
17659 var i = this.length++; // new context means new index
17660
17661 this[i] = {
17662 selector: selector,
17663 properties: [],
17664 mappedProperties: [],
17665 index: i
17666 };
17667 return this; // chaining
17668}; // add one or many css rules to the current context
17669
17670
17671styfn$8.css = function () {
17672 var self = this;
17673 var args = arguments;
17674
17675 if (args.length === 1) {
17676 var map = args[0];
17677
17678 for (var i = 0; i < self.properties.length; i++) {
17679 var prop = self.properties[i];
17680 var mapVal = map[prop.name];
17681
17682 if (mapVal === undefined) {
17683 mapVal = map[dash2camel(prop.name)];
17684 }
17685
17686 if (mapVal !== undefined) {
17687 this.cssRule(prop.name, mapVal);
17688 }
17689 }
17690 } else if (args.length === 2) {
17691 this.cssRule(args[0], args[1]);
17692 } // do nothing if args are invalid
17693
17694
17695 return this; // chaining
17696};
17697
17698styfn$8.style = styfn$8.css; // add a single css rule to the current context
17699
17700styfn$8.cssRule = function (name, value) {
17701 // name-value pair
17702 var property = this.parse(name, value); // add property to current context if valid
17703
17704 if (property) {
17705 var i = this.length - 1;
17706 this[i].properties.push(property);
17707 this[i].properties[property.name] = property; // allow access by name as well
17708
17709 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
17710 this._private.hasPie = true;
17711 }
17712
17713 if (property.mapped) {
17714 this[i].mappedProperties.push(property);
17715 } // add to core style if necessary
17716
17717
17718 var currentSelectorIsCore = !this[i].selector;
17719
17720 if (currentSelectorIsCore) {
17721 this._private.coreStyle[property.name] = property;
17722 }
17723 }
17724
17725 return this; // chaining
17726};
17727
17728styfn$8.append = function (style) {
17729 if (stylesheet(style)) {
17730 style.appendToStyle(this);
17731 } else if (array(style)) {
17732 this.appendFromJson(style);
17733 } else if (string(style)) {
17734 this.appendFromString(style);
17735 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
17736
17737
17738 return this;
17739}; // static function
17740
17741
17742Style.fromJson = function (cy, json) {
17743 var style = new Style(cy);
17744 style.fromJson(json);
17745 return style;
17746};
17747
17748Style.fromString = function (cy, string) {
17749 return new Style(cy).fromString(string);
17750};
17751
17752[styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
17753 extend(styfn$8, props);
17754});
17755Style.types = styfn$8.types;
17756Style.properties = styfn$8.properties;
17757Style.propertyGroups = styfn$8.propertyGroups;
17758Style.propertyGroupNames = styfn$8.propertyGroupNames;
17759Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
17760
17761var corefn$7 = {
17762 style: function style(newStyle) {
17763 if (newStyle) {
17764 var s = this.setStyle(newStyle);
17765 s.update();
17766 }
17767
17768 return this._private.style;
17769 },
17770 setStyle: function setStyle(style) {
17771 var _p = this._private;
17772
17773 if (stylesheet(style)) {
17774 _p.style = style.generateStyle(this);
17775 } else if (array(style)) {
17776 _p.style = Style.fromJson(this, style);
17777 } else if (string(style)) {
17778 _p.style = Style.fromString(this, style);
17779 } else {
17780 _p.style = Style(this);
17781 }
17782
17783 return _p.style;
17784 },
17785 // e.g. cy.data() changed => recalc ele mappers
17786 updateStyle: function updateStyle() {
17787 this.mutableElements().updateStyle(); // just send to all eles
17788 }
17789};
17790
17791var defaultSelectionType = 'single';
17792var corefn$8 = {
17793 autolock: function autolock(bool) {
17794 if (bool !== undefined) {
17795 this._private.autolock = bool ? true : false;
17796 } else {
17797 return this._private.autolock;
17798 }
17799
17800 return this; // chaining
17801 },
17802 autoungrabify: function autoungrabify(bool) {
17803 if (bool !== undefined) {
17804 this._private.autoungrabify = bool ? true : false;
17805 } else {
17806 return this._private.autoungrabify;
17807 }
17808
17809 return this; // chaining
17810 },
17811 autounselectify: function autounselectify(bool) {
17812 if (bool !== undefined) {
17813 this._private.autounselectify = bool ? true : false;
17814 } else {
17815 return this._private.autounselectify;
17816 }
17817
17818 return this; // chaining
17819 },
17820 selectionType: function selectionType(selType) {
17821 var _p = this._private;
17822
17823 if (_p.selectionType == null) {
17824 _p.selectionType = defaultSelectionType;
17825 }
17826
17827 if (selType !== undefined) {
17828 if (selType === 'additive' || selType === 'single') {
17829 _p.selectionType = selType;
17830 }
17831 } else {
17832 return _p.selectionType;
17833 }
17834
17835 return this;
17836 },
17837 panningEnabled: function panningEnabled(bool) {
17838 if (bool !== undefined) {
17839 this._private.panningEnabled = bool ? true : false;
17840 } else {
17841 return this._private.panningEnabled;
17842 }
17843
17844 return this; // chaining
17845 },
17846 userPanningEnabled: function userPanningEnabled(bool) {
17847 if (bool !== undefined) {
17848 this._private.userPanningEnabled = bool ? true : false;
17849 } else {
17850 return this._private.userPanningEnabled;
17851 }
17852
17853 return this; // chaining
17854 },
17855 zoomingEnabled: function zoomingEnabled(bool) {
17856 if (bool !== undefined) {
17857 this._private.zoomingEnabled = bool ? true : false;
17858 } else {
17859 return this._private.zoomingEnabled;
17860 }
17861
17862 return this; // chaining
17863 },
17864 userZoomingEnabled: function userZoomingEnabled(bool) {
17865 if (bool !== undefined) {
17866 this._private.userZoomingEnabled = bool ? true : false;
17867 } else {
17868 return this._private.userZoomingEnabled;
17869 }
17870
17871 return this; // chaining
17872 },
17873 boxSelectionEnabled: function boxSelectionEnabled(bool) {
17874 if (bool !== undefined) {
17875 this._private.boxSelectionEnabled = bool ? true : false;
17876 } else {
17877 return this._private.boxSelectionEnabled;
17878 }
17879
17880 return this; // chaining
17881 },
17882 pan: function pan() {
17883 var args = arguments;
17884 var pan = this._private.pan;
17885 var dim, val, dims, x, y;
17886
17887 switch (args.length) {
17888 case 0:
17889 // .pan()
17890 return pan;
17891
17892 case 1:
17893 if (string(args[0])) {
17894 // .pan('x')
17895 dim = args[0];
17896 return pan[dim];
17897 } else if (plainObject(args[0])) {
17898 // .pan({ x: 0, y: 100 })
17899 if (!this._private.panningEnabled) {
17900 return this;
17901 }
17902
17903 dims = args[0];
17904 x = dims.x;
17905 y = dims.y;
17906
17907 if (number(x)) {
17908 pan.x = x;
17909 }
17910
17911 if (number(y)) {
17912 pan.y = y;
17913 }
17914
17915 this.emit('pan viewport');
17916 }
17917
17918 break;
17919
17920 case 2:
17921 // .pan('x', 100)
17922 if (!this._private.panningEnabled) {
17923 return this;
17924 }
17925
17926 dim = args[0];
17927 val = args[1];
17928
17929 if ((dim === 'x' || dim === 'y') && number(val)) {
17930 pan[dim] = val;
17931 }
17932
17933 this.emit('pan viewport');
17934 break;
17935 // invalid
17936 }
17937
17938 this.notify('viewport');
17939 return this; // chaining
17940 },
17941 panBy: function panBy(arg0, arg1) {
17942 var args = arguments;
17943 var pan = this._private.pan;
17944 var dim, val, dims, x, y;
17945
17946 if (!this._private.panningEnabled) {
17947 return this;
17948 }
17949
17950 switch (args.length) {
17951 case 1:
17952 if (plainObject(arg0)) {
17953 // .panBy({ x: 0, y: 100 })
17954 dims = args[0];
17955 x = dims.x;
17956 y = dims.y;
17957
17958 if (number(x)) {
17959 pan.x += x;
17960 }
17961
17962 if (number(y)) {
17963 pan.y += y;
17964 }
17965
17966 this.emit('pan viewport');
17967 }
17968
17969 break;
17970
17971 case 2:
17972 // .panBy('x', 100)
17973 dim = arg0;
17974 val = arg1;
17975
17976 if ((dim === 'x' || dim === 'y') && number(val)) {
17977 pan[dim] += val;
17978 }
17979
17980 this.emit('pan viewport');
17981 break;
17982 // invalid
17983 }
17984
17985 this.notify('viewport');
17986 return this; // chaining
17987 },
17988 fit: function fit(elements, padding) {
17989 var viewportState = this.getFitViewport(elements, padding);
17990
17991 if (viewportState) {
17992 var _p = this._private;
17993 _p.zoom = viewportState.zoom;
17994 _p.pan = viewportState.pan;
17995 this.emit('pan zoom viewport');
17996 this.notify('viewport');
17997 }
17998
17999 return this; // chaining
18000 },
18001 getFitViewport: function getFitViewport(elements, padding) {
18002 if (number(elements) && padding === undefined) {
18003 // elements is optional
18004 padding = elements;
18005 elements = undefined;
18006 }
18007
18008 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18009 return;
18010 }
18011
18012 var bb;
18013
18014 if (string(elements)) {
18015 var sel = elements;
18016 elements = this.$(sel);
18017 } else if (boundingBox(elements)) {
18018 // assume bb
18019 var bbe = elements;
18020 bb = {
18021 x1: bbe.x1,
18022 y1: bbe.y1,
18023 x2: bbe.x2,
18024 y2: bbe.y2
18025 };
18026 bb.w = bb.x2 - bb.x1;
18027 bb.h = bb.y2 - bb.y1;
18028 } else if (!elementOrCollection(elements)) {
18029 elements = this.mutableElements();
18030 }
18031
18032 if (elementOrCollection(elements) && elements.empty()) {
18033 return;
18034 } // can't fit to nothing
18035
18036
18037 bb = bb || elements.boundingBox();
18038 var w = this.width();
18039 var h = this.height();
18040 var zoom;
18041 padding = number(padding) ? padding : 0;
18042
18043 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18044 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18045
18046 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18047 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18048 var pan = {
18049 // now pan to middle
18050 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18051 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18052 };
18053 return {
18054 zoom: zoom,
18055 pan: pan
18056 };
18057 }
18058
18059 return;
18060 },
18061 zoomRange: function zoomRange(min, max) {
18062 var _p = this._private;
18063
18064 if (max == null) {
18065 var opts = min;
18066 min = opts.min;
18067 max = opts.max;
18068 }
18069
18070 if (number(min) && number(max) && min <= max) {
18071 _p.minZoom = min;
18072 _p.maxZoom = max;
18073 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
18074 _p.minZoom = min;
18075 } else if (number(max) && min === undefined && max >= _p.minZoom) {
18076 _p.maxZoom = max;
18077 }
18078
18079 return this;
18080 },
18081 minZoom: function minZoom(zoom) {
18082 if (zoom === undefined) {
18083 return this._private.minZoom;
18084 } else {
18085 return this.zoomRange({
18086 min: zoom
18087 });
18088 }
18089 },
18090 maxZoom: function maxZoom(zoom) {
18091 if (zoom === undefined) {
18092 return this._private.maxZoom;
18093 } else {
18094 return this.zoomRange({
18095 max: zoom
18096 });
18097 }
18098 },
18099 getZoomedViewport: function getZoomedViewport(params) {
18100 var _p = this._private;
18101 var currentPan = _p.pan;
18102 var currentZoom = _p.zoom;
18103 var pos; // in rendered px
18104
18105 var zoom;
18106 var bail = false;
18107
18108 if (!_p.zoomingEnabled) {
18109 // zooming disabled
18110 bail = true;
18111 }
18112
18113 if (number(params)) {
18114 // then set the zoom
18115 zoom = params;
18116 } else if (plainObject(params)) {
18117 // then zoom about a point
18118 zoom = params.level;
18119
18120 if (params.position != null) {
18121 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18122 } else if (params.renderedPosition != null) {
18123 pos = params.renderedPosition;
18124 }
18125
18126 if (pos != null && !_p.panningEnabled) {
18127 // panning disabled
18128 bail = true;
18129 }
18130 } // crop zoom
18131
18132
18133 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18134 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18135
18136 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
18137 return null;
18138 }
18139
18140 if (pos != null) {
18141 // set zoom about position
18142 var pan1 = currentPan;
18143 var zoom1 = currentZoom;
18144 var zoom2 = zoom;
18145 var pan2 = {
18146 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18147 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18148 };
18149 return {
18150 zoomed: true,
18151 panned: true,
18152 zoom: zoom2,
18153 pan: pan2
18154 };
18155 } else {
18156 // just set the zoom
18157 return {
18158 zoomed: true,
18159 panned: false,
18160 zoom: zoom,
18161 pan: currentPan
18162 };
18163 }
18164 },
18165 zoom: function zoom(params) {
18166 if (params === undefined) {
18167 // get
18168 return this._private.zoom;
18169 } else {
18170 // set
18171 var vp = this.getZoomedViewport(params);
18172 var _p = this._private;
18173
18174 if (vp == null || !vp.zoomed) {
18175 return this;
18176 }
18177
18178 _p.zoom = vp.zoom;
18179
18180 if (vp.panned) {
18181 _p.pan.x = vp.pan.x;
18182 _p.pan.y = vp.pan.y;
18183 }
18184
18185 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18186 this.notify('viewport');
18187 return this; // chaining
18188 }
18189 },
18190 viewport: function viewport(opts) {
18191 var _p = this._private;
18192 var zoomDefd = true;
18193 var panDefd = true;
18194 var events = []; // to trigger
18195
18196 var zoomFailed = false;
18197 var panFailed = false;
18198
18199 if (!opts) {
18200 return this;
18201 }
18202
18203 if (!number(opts.zoom)) {
18204 zoomDefd = false;
18205 }
18206
18207 if (!plainObject(opts.pan)) {
18208 panDefd = false;
18209 }
18210
18211 if (!zoomDefd && !panDefd) {
18212 return this;
18213 }
18214
18215 if (zoomDefd) {
18216 var z = opts.zoom;
18217
18218 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18219 zoomFailed = true;
18220 } else {
18221 _p.zoom = z;
18222 events.push('zoom');
18223 }
18224 }
18225
18226 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18227 var p = opts.pan;
18228
18229 if (number(p.x)) {
18230 _p.pan.x = p.x;
18231 panFailed = false;
18232 }
18233
18234 if (number(p.y)) {
18235 _p.pan.y = p.y;
18236 panFailed = false;
18237 }
18238
18239 if (!panFailed) {
18240 events.push('pan');
18241 }
18242 }
18243
18244 if (events.length > 0) {
18245 events.push('viewport');
18246 this.emit(events.join(' '));
18247 this.notify('viewport');
18248 }
18249
18250 return this; // chaining
18251 },
18252 center: function center(elements) {
18253 var pan = this.getCenterPan(elements);
18254
18255 if (pan) {
18256 this._private.pan = pan;
18257 this.emit('pan viewport');
18258 this.notify('viewport');
18259 }
18260
18261 return this; // chaining
18262 },
18263 getCenterPan: function getCenterPan(elements, zoom) {
18264 if (!this._private.panningEnabled) {
18265 return;
18266 }
18267
18268 if (string(elements)) {
18269 var selector = elements;
18270 elements = this.mutableElements().filter(selector);
18271 } else if (!elementOrCollection(elements)) {
18272 elements = this.mutableElements();
18273 }
18274
18275 if (elements.length === 0) {
18276 return;
18277 } // can't centre pan to nothing
18278
18279
18280 var bb = elements.boundingBox();
18281 var w = this.width();
18282 var h = this.height();
18283 zoom = zoom === undefined ? this._private.zoom : zoom;
18284 var pan = {
18285 // middle
18286 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18287 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18288 };
18289 return pan;
18290 },
18291 reset: function reset() {
18292 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18293 return this;
18294 }
18295
18296 this.viewport({
18297 pan: {
18298 x: 0,
18299 y: 0
18300 },
18301 zoom: 1
18302 });
18303 return this; // chaining
18304 },
18305 invalidateSize: function invalidateSize() {
18306 this._private.sizeCache = null;
18307 },
18308 size: function size() {
18309 var _p = this._private;
18310 var container = _p.container;
18311 return _p.sizeCache = _p.sizeCache || (container ? function () {
18312 var style = window$1.getComputedStyle(container);
18313
18314 var val = function val(name) {
18315 return parseFloat(style.getPropertyValue(name));
18316 };
18317
18318 return {
18319 width: container.clientWidth - val('padding-left') - val('padding-right'),
18320 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18321 };
18322 }() : {
18323 // fallback if no container (not 0 b/c can be used for dividing etc)
18324 width: 1,
18325 height: 1
18326 });
18327 },
18328 width: function width() {
18329 return this.size().width;
18330 },
18331 height: function height() {
18332 return this.size().height;
18333 },
18334 extent: function extent() {
18335 var pan = this._private.pan;
18336 var zoom = this._private.zoom;
18337 var rb = this.renderedExtent();
18338 var b = {
18339 x1: (rb.x1 - pan.x) / zoom,
18340 x2: (rb.x2 - pan.x) / zoom,
18341 y1: (rb.y1 - pan.y) / zoom,
18342 y2: (rb.y2 - pan.y) / zoom
18343 };
18344 b.w = b.x2 - b.x1;
18345 b.h = b.y2 - b.y1;
18346 return b;
18347 },
18348 renderedExtent: function renderedExtent() {
18349 var width = this.width();
18350 var height = this.height();
18351 return {
18352 x1: 0,
18353 y1: 0,
18354 x2: width,
18355 y2: height,
18356 w: width,
18357 h: height
18358 };
18359 }
18360}; // aliases
18361
18362corefn$8.centre = corefn$8.center; // backwards compatibility
18363
18364corefn$8.autolockNodes = corefn$8.autolock;
18365corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
18366
18367var fn$6 = {
18368 data: define$3.data({
18369 field: 'data',
18370 bindingEvent: 'data',
18371 allowBinding: true,
18372 allowSetting: true,
18373 settingEvent: 'data',
18374 settingTriggersEvent: true,
18375 triggerFnName: 'trigger',
18376 allowGetting: true,
18377 updateStyle: true
18378 }),
18379 removeData: define$3.removeData({
18380 field: 'data',
18381 event: 'data',
18382 triggerFnName: 'trigger',
18383 triggerEvent: true,
18384 updateStyle: true
18385 }),
18386 scratch: define$3.data({
18387 field: 'scratch',
18388 bindingEvent: 'scratch',
18389 allowBinding: true,
18390 allowSetting: true,
18391 settingEvent: 'scratch',
18392 settingTriggersEvent: true,
18393 triggerFnName: 'trigger',
18394 allowGetting: true,
18395 updateStyle: true
18396 }),
18397 removeScratch: define$3.removeData({
18398 field: 'scratch',
18399 event: 'scratch',
18400 triggerFnName: 'trigger',
18401 triggerEvent: true,
18402 updateStyle: true
18403 })
18404}; // aliases
18405
18406fn$6.attr = fn$6.data;
18407fn$6.removeAttr = fn$6.removeData;
18408
18409var Core = function Core(opts) {
18410 var cy = this;
18411 opts = extend({}, opts);
18412 var container = opts.container; // allow for passing a wrapped jquery object
18413 // e.g. cytoscape({ container: $('#cy') })
18414
18415 if (container && !htmlElement(container) && htmlElement(container[0])) {
18416 container = container[0];
18417 }
18418
18419 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18420
18421 reg = reg || {};
18422
18423 if (reg && reg.cy) {
18424 reg.cy.destroy();
18425 reg = {}; // old instance => replace reg completely
18426 }
18427
18428 var readies = reg.readies = reg.readies || [];
18429
18430 if (container) {
18431 container._cyreg = reg;
18432 } // make sure container assoc'd reg points to this cy
18433
18434
18435 reg.cy = cy;
18436 var head = window$1 !== undefined && container !== undefined && !opts.headless;
18437 var options = opts;
18438 options.layout = extend({
18439 name: head ? 'grid' : 'null'
18440 }, options.layout);
18441 options.renderer = extend({
18442 name: head ? 'canvas' : 'null'
18443 }, options.renderer);
18444
18445 var defVal = function defVal(def, val, altVal) {
18446 if (val !== undefined) {
18447 return val;
18448 } else if (altVal !== undefined) {
18449 return altVal;
18450 } else {
18451 return def;
18452 }
18453 };
18454
18455 var _p = this._private = {
18456 container: container,
18457 // html dom ele container
18458 ready: false,
18459 // whether ready has been triggered
18460 options: options,
18461 // cached options
18462 elements: new Collection(this),
18463 // elements in the graph
18464 listeners: [],
18465 // list of listeners
18466 aniEles: new Collection(this),
18467 // elements being animated
18468 data: options.data || {},
18469 // data for the core
18470 scratch: {},
18471 // scratch object for core
18472 layout: null,
18473 renderer: null,
18474 destroyed: false,
18475 // whether destroy was called
18476 notificationsEnabled: true,
18477 // whether notifications are sent to the renderer
18478 minZoom: 1e-50,
18479 maxZoom: 1e50,
18480 zoomingEnabled: defVal(true, options.zoomingEnabled),
18481 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18482 panningEnabled: defVal(true, options.panningEnabled),
18483 userPanningEnabled: defVal(true, options.userPanningEnabled),
18484 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18485 autolock: defVal(false, options.autolock, options.autolockNodes),
18486 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18487 autounselectify: defVal(false, options.autounselectify),
18488 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18489 zoom: number(options.zoom) ? options.zoom : 1,
18490 pan: {
18491 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
18492 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
18493 },
18494 animation: {
18495 // object for currently-running animations
18496 current: [],
18497 queue: []
18498 },
18499 hasCompoundNodes: false
18500 };
18501
18502 this.createEmitter(); // set selection type
18503
18504 this.selectionType(options.selectionType); // init zoom bounds
18505
18506 this.zoomRange({
18507 min: options.minZoom,
18508 max: options.maxZoom
18509 });
18510
18511 var loadExtData = function loadExtData(extData, next) {
18512 var anyIsPromise = extData.some(promise);
18513
18514 if (anyIsPromise) {
18515 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18516 } else {
18517 next(extData); // exec synchronously for convenience
18518 }
18519 }; // start with the default stylesheet so we have something before loading an external stylesheet
18520
18521
18522 if (_p.styleEnabled) {
18523 cy.setStyle([]);
18524 } // create the renderer
18525
18526
18527 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18528
18529 cy.initRenderer(rendererOptions);
18530
18531 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18532 cy.notifications(false); // remove old elements
18533
18534 var oldEles = cy.mutableElements();
18535
18536 if (oldEles.length > 0) {
18537 oldEles.remove();
18538 }
18539
18540 if (elements != null) {
18541 if (plainObject(elements) || array(elements)) {
18542 cy.add(elements);
18543 }
18544 }
18545
18546 cy.one('layoutready', function (e) {
18547 cy.notifications(true);
18548 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18549
18550 cy.one('load', onload);
18551 cy.emitAndNotify('load');
18552 }).one('layoutstop', function () {
18553 cy.one('done', ondone);
18554 cy.emit('done');
18555 });
18556 var layoutOpts = extend({}, cy._private.options.layout);
18557 layoutOpts.eles = cy.elements();
18558 cy.layout(layoutOpts).run();
18559 };
18560
18561 loadExtData([options.style, options.elements], function (thens) {
18562 var initStyle = thens[0];
18563 var initEles = thens[1]; // init style
18564
18565 if (_p.styleEnabled) {
18566 cy.style().append(initStyle);
18567 } // initial load
18568
18569
18570 setElesAndLayout(initEles, function () {
18571 // onready
18572 cy.startAnimationLoop();
18573 _p.ready = true; // if a ready callback is specified as an option, the bind it
18574
18575 if (fn(options.ready)) {
18576 cy.on('ready', options.ready);
18577 } // bind all the ready handlers registered before creating this instance
18578
18579
18580 for (var i = 0; i < readies.length; i++) {
18581 var fn$1 = readies[i];
18582 cy.on('ready', fn$1);
18583 }
18584
18585 if (reg) {
18586 reg.readies = [];
18587 } // 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
18588
18589
18590 cy.emit('ready');
18591 }, options.done);
18592 });
18593};
18594
18595var corefn$9 = Core.prototype; // short alias
18596
18597extend(corefn$9, {
18598 instanceString: function instanceString() {
18599 return 'core';
18600 },
18601 isReady: function isReady() {
18602 return this._private.ready;
18603 },
18604 destroyed: function destroyed() {
18605 return this._private.destroyed;
18606 },
18607 ready: function ready(fn) {
18608 if (this.isReady()) {
18609 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18610 } else {
18611 this.on('ready', fn);
18612 }
18613
18614 return this;
18615 },
18616 destroy: function destroy() {
18617 var cy = this;
18618 if (cy.destroyed()) return;
18619 cy.stopAnimationLoop();
18620 cy.destroyRenderer();
18621 this.emit('destroy');
18622 cy._private.destroyed = true;
18623 return cy;
18624 },
18625 hasElementWithId: function hasElementWithId(id) {
18626 return this._private.elements.hasElementWithId(id);
18627 },
18628 getElementById: function getElementById(id) {
18629 return this._private.elements.getElementById(id);
18630 },
18631 hasCompoundNodes: function hasCompoundNodes() {
18632 return this._private.hasCompoundNodes;
18633 },
18634 headless: function headless() {
18635 return this._private.renderer.isHeadless();
18636 },
18637 styleEnabled: function styleEnabled() {
18638 return this._private.styleEnabled;
18639 },
18640 addToPool: function addToPool(eles) {
18641 this._private.elements.merge(eles);
18642
18643 return this; // chaining
18644 },
18645 removeFromPool: function removeFromPool(eles) {
18646 this._private.elements.unmerge(eles);
18647
18648 return this;
18649 },
18650 container: function container() {
18651 return this._private.container || null;
18652 },
18653 mount: function mount(container) {
18654 if (container == null) {
18655 return;
18656 }
18657
18658 var cy = this;
18659 var _p = cy._private;
18660 var options = _p.options;
18661
18662 if (!htmlElement(container) && htmlElement(container[0])) {
18663 container = container[0];
18664 }
18665
18666 cy.stopAnimationLoop();
18667 cy.destroyRenderer();
18668 _p.container = container;
18669 _p.styleEnabled = true;
18670 cy.invalidateSize();
18671 cy.initRenderer(extend({}, options, options.renderer, {
18672 // allow custom renderer name to be re-used, otherwise use canvas
18673 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
18674 }));
18675 cy.startAnimationLoop();
18676 cy.style(options.style);
18677 cy.emit('mount');
18678 return cy;
18679 },
18680 unmount: function unmount() {
18681 var cy = this;
18682 cy.stopAnimationLoop();
18683 cy.destroyRenderer();
18684 cy.initRenderer({
18685 name: 'null'
18686 });
18687 cy.emit('unmount');
18688 return cy;
18689 },
18690 options: function options() {
18691 return copy(this._private.options);
18692 },
18693 json: function json(obj) {
18694 var cy = this;
18695 var _p = cy._private;
18696 var eles = cy.mutableElements();
18697
18698 var getFreshRef = function getFreshRef(ele) {
18699 return cy.getElementById(ele.id());
18700 };
18701
18702 if (plainObject(obj)) {
18703 // set
18704 cy.startBatch();
18705
18706 if (obj.elements) {
18707 var idInJson = {};
18708
18709 var updateEles = function updateEles(jsons, gr) {
18710 var toAdd = [];
18711 var toMod = [];
18712
18713 for (var i = 0; i < jsons.length; i++) {
18714 var json = jsons[i];
18715
18716 if (!json.data.id) {
18717 warn('cy.json() cannot handle elements without an ID attribute');
18718 continue;
18719 }
18720
18721 var id = '' + json.data.id; // id must be string
18722
18723 var ele = cy.getElementById(id);
18724 idInJson[id] = true;
18725
18726 if (ele.length !== 0) {
18727 // existing element should be updated
18728 toMod.push({
18729 ele: ele,
18730 json: json
18731 });
18732 } else {
18733 // otherwise should be added
18734 if (gr) {
18735 json.group = gr;
18736 toAdd.push(json);
18737 } else {
18738 toAdd.push(json);
18739 }
18740 }
18741 }
18742
18743 cy.add(toAdd);
18744
18745 for (var _i = 0; _i < toMod.length; _i++) {
18746 var _toMod$_i = toMod[_i],
18747 _ele = _toMod$_i.ele,
18748 _json = _toMod$_i.json;
18749
18750 _ele.json(_json);
18751 }
18752 };
18753
18754 if (array(obj.elements)) {
18755 // elements: []
18756 updateEles(obj.elements);
18757 } else {
18758 // elements: { nodes: [], edges: [] }
18759 var grs = ['nodes', 'edges'];
18760
18761 for (var i = 0; i < grs.length; i++) {
18762 var gr = grs[i];
18763 var elements = obj.elements[gr];
18764
18765 if (array(elements)) {
18766 updateEles(elements, gr);
18767 }
18768 }
18769 }
18770
18771 var parentsToRemove = cy.collection();
18772 eles.filter(function (ele) {
18773 return !idInJson[ele.id()];
18774 }).forEach(function (ele) {
18775 if (ele.isParent()) {
18776 parentsToRemove.merge(ele);
18777 } else {
18778 ele.remove();
18779 }
18780 }); // so that children are not removed w/parent
18781
18782 parentsToRemove.forEach(function (ele) {
18783 return ele.children().move({
18784 parent: null
18785 });
18786 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
18787
18788 parentsToRemove.forEach(function (ele) {
18789 return getFreshRef(ele).remove();
18790 });
18791 }
18792
18793 if (obj.style) {
18794 cy.style(obj.style);
18795 }
18796
18797 if (obj.zoom != null && obj.zoom !== _p.zoom) {
18798 cy.zoom(obj.zoom);
18799 }
18800
18801 if (obj.pan) {
18802 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
18803 cy.pan(obj.pan);
18804 }
18805 }
18806
18807 if (obj.data) {
18808 cy.data(obj.data);
18809 }
18810
18811 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
18812
18813 for (var _i2 = 0; _i2 < fields.length; _i2++) {
18814 var f = fields[_i2];
18815
18816 if (obj[f] != null) {
18817 cy[f](obj[f]);
18818 }
18819 }
18820
18821 cy.endBatch();
18822 return this; // chaining
18823 } else {
18824 // get
18825 var flat = !!obj;
18826 var json = {};
18827
18828 if (flat) {
18829 json.elements = this.elements().map(function (ele) {
18830 return ele.json();
18831 });
18832 } else {
18833 json.elements = {};
18834 eles.forEach(function (ele) {
18835 var group = ele.group();
18836
18837 if (!json.elements[group]) {
18838 json.elements[group] = [];
18839 }
18840
18841 json.elements[group].push(ele.json());
18842 });
18843 }
18844
18845 if (this._private.styleEnabled) {
18846 json.style = cy.style().json();
18847 }
18848
18849 json.data = copy(cy.data());
18850 var options = _p.options;
18851 json.zoomingEnabled = _p.zoomingEnabled;
18852 json.userZoomingEnabled = _p.userZoomingEnabled;
18853 json.zoom = _p.zoom;
18854 json.minZoom = _p.minZoom;
18855 json.maxZoom = _p.maxZoom;
18856 json.panningEnabled = _p.panningEnabled;
18857 json.userPanningEnabled = _p.userPanningEnabled;
18858 json.pan = copy(_p.pan);
18859 json.boxSelectionEnabled = _p.boxSelectionEnabled;
18860 json.renderer = copy(options.renderer);
18861 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
18862 json.textureOnViewport = options.textureOnViewport;
18863 json.wheelSensitivity = options.wheelSensitivity;
18864 json.motionBlur = options.motionBlur;
18865 return json;
18866 }
18867 }
18868});
18869corefn$9.$id = corefn$9.getElementById;
18870[corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
18871 extend(corefn$9, props);
18872});
18873
18874/* eslint-disable no-unused-vars */
18875
18876var defaults$9 = {
18877 fit: true,
18878 // whether to fit the viewport to the graph
18879 directed: false,
18880 // whether the tree is directed downwards (or edges can point in any direction if false)
18881 padding: 30,
18882 // padding on fit
18883 circle: false,
18884 // put depths in concentric circles if true, put depths top down if false
18885 grid: false,
18886 // whether to create an even grid into which the DAG is placed (circle:false only)
18887 spacingFactor: 1.75,
18888 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
18889 boundingBox: undefined,
18890 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
18891 avoidOverlap: true,
18892 // prevents node overlap, may overflow boundingBox if not enough space
18893 nodeDimensionsIncludeLabels: false,
18894 // Excludes the label when calculating node bounding boxes for the layout algorithm
18895 roots: undefined,
18896 // the roots of the trees
18897 maximal: false,
18898 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
18899 animate: false,
18900 // whether to transition the node positions
18901 animationDuration: 500,
18902 // duration of animation in ms if enabled
18903 animationEasing: undefined,
18904 // easing of animation if enabled,
18905 animateFilter: function animateFilter(node, i) {
18906 return true;
18907 },
18908 // 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
18909 ready: undefined,
18910 // callback on layoutready
18911 stop: undefined,
18912 // callback on layoutstop
18913 transform: function transform(node, position) {
18914 return position;
18915 } // transform a given node position. Useful for changing flow direction in discrete layouts
18916
18917};
18918/* eslint-enable */
18919
18920var getInfo = function getInfo(ele) {
18921 return ele.scratch('breadthfirst');
18922};
18923
18924var setInfo = function setInfo(ele, obj) {
18925 return ele.scratch('breadthfirst', obj);
18926};
18927
18928function BreadthFirstLayout(options) {
18929 this.options = extend({}, defaults$9, options);
18930}
18931
18932BreadthFirstLayout.prototype.run = function () {
18933 var params = this.options;
18934 var options = params;
18935 var cy = params.cy;
18936 var eles = options.eles;
18937 var nodes = eles.nodes().filter(function (n) {
18938 return !n.isParent();
18939 });
18940 var graph = eles;
18941 var directed = options.directed;
18942 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
18943
18944 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
18945 x1: 0,
18946 y1: 0,
18947 w: cy.width(),
18948 h: cy.height()
18949 });
18950 var roots;
18951
18952 if (elementOrCollection(options.roots)) {
18953 roots = options.roots;
18954 } else if (array(options.roots)) {
18955 var rootsArray = [];
18956
18957 for (var i = 0; i < options.roots.length; i++) {
18958 var id = options.roots[i];
18959 var ele = cy.getElementById(id);
18960 rootsArray.push(ele);
18961 }
18962
18963 roots = cy.collection(rootsArray);
18964 } else if (string(options.roots)) {
18965 roots = cy.$(options.roots);
18966 } else {
18967 if (directed) {
18968 roots = nodes.roots();
18969 } else {
18970 var components = eles.components();
18971 roots = cy.collection();
18972
18973 var _loop = function _loop(_i) {
18974 var comp = components[_i];
18975 var maxDegree = comp.maxDegree(false);
18976 var compRoots = comp.filter(function (ele) {
18977 return ele.degree(false) === maxDegree;
18978 });
18979 roots = roots.add(compRoots);
18980 };
18981
18982 for (var _i = 0; _i < components.length; _i++) {
18983 _loop(_i);
18984 }
18985 }
18986 }
18987
18988 var depths = [];
18989 var foundByBfs = {};
18990
18991 var addToDepth = function addToDepth(ele, d) {
18992 if (depths[d] == null) {
18993 depths[d] = [];
18994 }
18995
18996 var i = depths[d].length;
18997 depths[d].push(ele);
18998 setInfo(ele, {
18999 index: i,
19000 depth: d
19001 });
19002 };
19003
19004 var changeDepth = function changeDepth(ele, newDepth) {
19005 var _getInfo = getInfo(ele),
19006 depth = _getInfo.depth,
19007 index = _getInfo.index;
19008
19009 depths[depth][index] = null;
19010 addToDepth(ele, newDepth);
19011 }; // find the depths of the nodes
19012
19013
19014 graph.bfs({
19015 roots: roots,
19016 directed: options.directed,
19017 visit: function visit(node, edge, pNode, i, depth) {
19018 var ele = node[0];
19019 var id = ele.id();
19020 addToDepth(ele, depth);
19021 foundByBfs[id] = true;
19022 }
19023 }); // check for nodes not found by bfs
19024
19025 var orphanNodes = [];
19026
19027 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19028 var _ele = nodes[_i2];
19029
19030 if (foundByBfs[_ele.id()]) {
19031 continue;
19032 } else {
19033 orphanNodes.push(_ele);
19034 }
19035 } // assign the nodes a depth and index
19036
19037
19038 var assignDepthsAt = function assignDepthsAt(i) {
19039 var eles = depths[i];
19040
19041 for (var j = 0; j < eles.length; j++) {
19042 var _ele2 = eles[j];
19043
19044 if (_ele2 == null) {
19045 eles.splice(j, 1);
19046 j--;
19047 continue;
19048 }
19049
19050 setInfo(_ele2, {
19051 depth: i,
19052 index: j
19053 });
19054 }
19055 };
19056
19057 var assignDepths = function assignDepths() {
19058 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19059 assignDepthsAt(_i3);
19060 }
19061 };
19062
19063 var adjustMaximally = function adjustMaximally(ele, shifted) {
19064 var eInfo = getInfo(ele);
19065 var incomers = ele.incomers().filter(function (el) {
19066 return el.isNode() && eles.has(el);
19067 });
19068 var maxDepth = -1;
19069 var id = ele.id();
19070
19071 for (var k = 0; k < incomers.length; k++) {
19072 var incmr = incomers[k];
19073 var iInfo = getInfo(incmr);
19074 maxDepth = Math.max(maxDepth, iInfo.depth);
19075 }
19076
19077 if (eInfo.depth <= maxDepth) {
19078 if (shifted[id]) {
19079 return null;
19080 }
19081
19082 changeDepth(ele, maxDepth + 1);
19083 shifted[id] = true;
19084 return true;
19085 }
19086
19087 return false;
19088 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19089
19090
19091 if (directed && maximal) {
19092 var Q = [];
19093 var shifted = {};
19094
19095 var enqueue = function enqueue(n) {
19096 return Q.push(n);
19097 };
19098
19099 var dequeue = function dequeue() {
19100 return Q.shift();
19101 };
19102
19103 nodes.forEach(function (n) {
19104 return Q.push(n);
19105 });
19106
19107 while (Q.length > 0) {
19108 var _ele3 = dequeue();
19109
19110 var didShift = adjustMaximally(_ele3, shifted);
19111
19112 if (didShift) {
19113 _ele3.outgoers().filter(function (el) {
19114 return el.isNode() && eles.has(el);
19115 }).forEach(enqueue);
19116 } else if (didShift === null) {
19117 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19118 break; // exit on failure
19119 }
19120 }
19121 }
19122
19123 assignDepths(); // clear holes
19124 // find min distance we need to leave between nodes
19125
19126 var minDistance = 0;
19127
19128 if (options.avoidOverlap) {
19129 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19130 var n = nodes[_i4];
19131 var nbb = n.layoutDimensions(options);
19132 var w = nbb.w;
19133 var h = nbb.h;
19134 minDistance = Math.max(minDistance, w, h);
19135 }
19136 } // get the weighted percent for an element based on its connectivity to other levels
19137
19138
19139 var cachedWeightedPercent = {};
19140
19141 var getWeightedPercent = function getWeightedPercent(ele) {
19142 if (cachedWeightedPercent[ele.id()]) {
19143 return cachedWeightedPercent[ele.id()];
19144 }
19145
19146 var eleDepth = getInfo(ele).depth;
19147 var neighbors = ele.neighborhood();
19148 var percent = 0;
19149 var samples = 0;
19150
19151 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19152 var neighbor = neighbors[_i5];
19153
19154 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19155 continue;
19156 }
19157
19158 var bf = getInfo(neighbor);
19159
19160 if (bf == null) {
19161 continue;
19162 }
19163
19164 var index = bf.index;
19165 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19166
19167 if (index == null || depth == null) {
19168 continue;
19169 }
19170
19171 var nDepth = depths[depth].length;
19172
19173 if (depth < eleDepth) {
19174 // only get influenced by elements above
19175 percent += index / nDepth;
19176 samples++;
19177 }
19178 }
19179
19180 samples = Math.max(1, samples);
19181 percent = percent / samples;
19182
19183 if (samples === 0) {
19184 // put lone nodes at the start
19185 percent = 0;
19186 }
19187
19188 cachedWeightedPercent[ele.id()] = percent;
19189 return percent;
19190 }; // rearrange the indices in each depth level based on connectivity
19191
19192
19193 var sortFn = function sortFn(a, b) {
19194 var apct = getWeightedPercent(a);
19195 var bpct = getWeightedPercent(b);
19196 var diff = apct - bpct;
19197
19198 if (diff === 0) {
19199 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19200 } else {
19201 return diff;
19202 }
19203 }; // sort each level to make connected nodes closer
19204
19205
19206 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19207 depths[_i6].sort(sortFn);
19208
19209 assignDepthsAt(_i6);
19210 } // assign orphan nodes to a new top-level depth
19211
19212
19213 var orphanDepth = [];
19214
19215 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19216 orphanDepth.push(orphanNodes[_i7]);
19217 }
19218
19219 depths.unshift(orphanDepth);
19220 assignDepths();
19221 var biggestDepthSize = 0;
19222
19223 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19224 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19225 }
19226
19227 var center = {
19228 x: bb.x1 + bb.w / 2,
19229 y: bb.x1 + bb.h / 2
19230 };
19231 var maxDepthSize = depths.reduce(function (max, eles) {
19232 return Math.max(max, eles.length);
19233 }, 0);
19234
19235 var getPosition = function getPosition(ele) {
19236 var _getInfo2 = getInfo(ele),
19237 depth = _getInfo2.depth,
19238 index = _getInfo2.index;
19239
19240 var depthSize = depths[depth].length;
19241 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19242 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19243 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19244 radiusStepSize = Math.max(radiusStepSize, minDistance);
19245
19246 if (!options.circle) {
19247 var epos = {
19248 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19249 y: (depth + 1) * distanceY
19250 };
19251 return epos;
19252 } else {
19253 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19254 var theta = 2 * Math.PI / depths[depth].length * index;
19255
19256 if (depth === 0 && depths[0].length === 1) {
19257 radius = 1;
19258 }
19259
19260 return {
19261 x: center.x + radius * Math.cos(theta),
19262 y: center.y + radius * Math.sin(theta)
19263 };
19264 }
19265 };
19266
19267 eles.nodes().layoutPositions(this, options, getPosition);
19268 return this; // chaining
19269};
19270
19271var defaults$a = {
19272 fit: true,
19273 // whether to fit the viewport to the graph
19274 padding: 30,
19275 // the padding on fit
19276 boundingBox: undefined,
19277 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19278 avoidOverlap: true,
19279 // prevents node overlap, may overflow boundingBox and radius if not enough space
19280 nodeDimensionsIncludeLabels: false,
19281 // Excludes the label when calculating node bounding boxes for the layout algorithm
19282 spacingFactor: undefined,
19283 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19284 radius: undefined,
19285 // the radius of the circle
19286 startAngle: 3 / 2 * Math.PI,
19287 // where nodes start in radians
19288 sweep: undefined,
19289 // how many radians should be between the first and last node (defaults to full circle)
19290 clockwise: true,
19291 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19292 sort: undefined,
19293 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19294 animate: false,
19295 // whether to transition the node positions
19296 animationDuration: 500,
19297 // duration of animation in ms if enabled
19298 animationEasing: undefined,
19299 // easing of animation if enabled
19300 animateFilter: function animateFilter(node, i) {
19301 return true;
19302 },
19303 // 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
19304 ready: undefined,
19305 // callback on layoutready
19306 stop: undefined,
19307 // callback on layoutstop
19308 transform: function transform(node, position) {
19309 return position;
19310 } // transform a given node position. Useful for changing flow direction in discrete layouts
19311
19312};
19313
19314function CircleLayout(options) {
19315 this.options = extend({}, defaults$a, options);
19316}
19317
19318CircleLayout.prototype.run = function () {
19319 var params = this.options;
19320 var options = params;
19321 var cy = params.cy;
19322 var eles = options.eles;
19323 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19324 var nodes = eles.nodes().not(':parent');
19325
19326 if (options.sort) {
19327 nodes = nodes.sort(options.sort);
19328 }
19329
19330 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19331 x1: 0,
19332 y1: 0,
19333 w: cy.width(),
19334 h: cy.height()
19335 });
19336 var center = {
19337 x: bb.x1 + bb.w / 2,
19338 y: bb.y1 + bb.h / 2
19339 };
19340 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19341 var dTheta = sweep / Math.max(1, nodes.length - 1);
19342 var r;
19343 var minDistance = 0;
19344
19345 for (var i = 0; i < nodes.length; i++) {
19346 var n = nodes[i];
19347 var nbb = n.layoutDimensions(options);
19348 var w = nbb.w;
19349 var h = nbb.h;
19350 minDistance = Math.max(minDistance, w, h);
19351 }
19352
19353 if (number(options.radius)) {
19354 r = options.radius;
19355 } else if (nodes.length <= 1) {
19356 r = 0;
19357 } else {
19358 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19359 } // calculate the radius
19360
19361
19362 if (nodes.length > 1 && options.avoidOverlap) {
19363 // but only if more than one node (can't overlap)
19364 minDistance *= 1.75; // just to have some nice spacing
19365
19366 var dcos = Math.cos(dTheta) - Math.cos(0);
19367 var dsin = Math.sin(dTheta) - Math.sin(0);
19368 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19369
19370 r = Math.max(rMin, r);
19371 }
19372
19373 var getPos = function getPos(ele, i) {
19374 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19375 var rx = r * Math.cos(theta);
19376 var ry = r * Math.sin(theta);
19377 var pos = {
19378 x: center.x + rx,
19379 y: center.y + ry
19380 };
19381 return pos;
19382 };
19383
19384 eles.nodes().layoutPositions(this, options, getPos);
19385 return this; // chaining
19386};
19387
19388var defaults$b = {
19389 fit: true,
19390 // whether to fit the viewport to the graph
19391 padding: 30,
19392 // the padding on fit
19393 startAngle: 3 / 2 * Math.PI,
19394 // where nodes start in radians
19395 sweep: undefined,
19396 // how many radians should be between the first and last node (defaults to full circle)
19397 clockwise: true,
19398 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19399 equidistant: false,
19400 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19401 minNodeSpacing: 10,
19402 // min spacing between outside of nodes (used for radius adjustment)
19403 boundingBox: undefined,
19404 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19405 avoidOverlap: true,
19406 // prevents node overlap, may overflow boundingBox if not enough space
19407 nodeDimensionsIncludeLabels: false,
19408 // Excludes the label when calculating node bounding boxes for the layout algorithm
19409 height: undefined,
19410 // height of layout area (overrides container height)
19411 width: undefined,
19412 // width of layout area (overrides container width)
19413 spacingFactor: undefined,
19414 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19415 concentric: function concentric(node) {
19416 // returns numeric value for each node, placing higher nodes in levels towards the centre
19417 return node.degree();
19418 },
19419 levelWidth: function levelWidth(nodes) {
19420 // the variation of concentric values in each level
19421 return nodes.maxDegree() / 4;
19422 },
19423 animate: false,
19424 // whether to transition the node positions
19425 animationDuration: 500,
19426 // duration of animation in ms if enabled
19427 animationEasing: undefined,
19428 // easing of animation if enabled
19429 animateFilter: function animateFilter(node, i) {
19430 return true;
19431 },
19432 // 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
19433 ready: undefined,
19434 // callback on layoutready
19435 stop: undefined,
19436 // callback on layoutstop
19437 transform: function transform(node, position) {
19438 return position;
19439 } // transform a given node position. Useful for changing flow direction in discrete layouts
19440
19441};
19442
19443function ConcentricLayout(options) {
19444 this.options = extend({}, defaults$b, options);
19445}
19446
19447ConcentricLayout.prototype.run = function () {
19448 var params = this.options;
19449 var options = params;
19450 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19451 var cy = params.cy;
19452 var eles = options.eles;
19453 var nodes = eles.nodes().not(':parent');
19454 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19455 x1: 0,
19456 y1: 0,
19457 w: cy.width(),
19458 h: cy.height()
19459 });
19460 var center = {
19461 x: bb.x1 + bb.w / 2,
19462 y: bb.y1 + bb.h / 2
19463 };
19464 var nodeValues = []; // { node, value }
19465
19466 var maxNodeSize = 0;
19467
19468 for (var i = 0; i < nodes.length; i++) {
19469 var node = nodes[i];
19470 var value = void 0; // calculate the node value
19471
19472 value = options.concentric(node);
19473 nodeValues.push({
19474 value: value,
19475 node: node
19476 }); // for style mapping
19477
19478 node._private.scratch.concentric = value;
19479 } // in case we used the `concentric` in style
19480
19481
19482 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
19483
19484 for (var _i = 0; _i < nodes.length; _i++) {
19485 var _node = nodes[_i];
19486
19487 var nbb = _node.layoutDimensions(options);
19488
19489 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19490 } // sort node values in descreasing order
19491
19492
19493 nodeValues.sort(function (a, b) {
19494 return b.value - a.value;
19495 });
19496 var levelWidth = options.levelWidth(nodes); // put the values into levels
19497
19498 var levels = [[]];
19499 var currentLevel = levels[0];
19500
19501 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19502 var val = nodeValues[_i2];
19503
19504 if (currentLevel.length > 0) {
19505 var diff = Math.abs(currentLevel[0].value - val.value);
19506
19507 if (diff >= levelWidth) {
19508 currentLevel = [];
19509 levels.push(currentLevel);
19510 }
19511 }
19512
19513 currentLevel.push(val);
19514 } // create positions from levels
19515
19516
19517 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19518
19519 if (!options.avoidOverlap) {
19520 // then strictly constrain to bb
19521 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19522 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19523 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19524 minDist = Math.min(minDist, rStep);
19525 } // find the metrics for each level
19526
19527
19528 var r = 0;
19529
19530 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19531 var level = levels[_i3];
19532 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19533 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
19534
19535 if (level.length > 1 && options.avoidOverlap) {
19536 // but only if more than one node (can't overlap)
19537 var dcos = Math.cos(dTheta) - Math.cos(0);
19538 var dsin = Math.sin(dTheta) - Math.sin(0);
19539 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19540
19541 r = Math.max(rMin, r);
19542 }
19543
19544 level.r = r;
19545 r += minDist;
19546 }
19547
19548 if (options.equidistant) {
19549 var rDeltaMax = 0;
19550 var _r = 0;
19551
19552 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19553 var _level = levels[_i4];
19554 var rDelta = _level.r - _r;
19555 rDeltaMax = Math.max(rDeltaMax, rDelta);
19556 }
19557
19558 _r = 0;
19559
19560 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19561 var _level2 = levels[_i5];
19562
19563 if (_i5 === 0) {
19564 _r = _level2.r;
19565 }
19566
19567 _level2.r = _r;
19568 _r += rDeltaMax;
19569 }
19570 } // calculate the node positions
19571
19572
19573 var pos = {}; // id => position
19574
19575 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19576 var _level3 = levels[_i6];
19577 var _dTheta = _level3.dTheta;
19578 var _r2 = _level3.r;
19579
19580 for (var j = 0; j < _level3.length; j++) {
19581 var _val = _level3[j];
19582 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19583 var p = {
19584 x: center.x + _r2 * Math.cos(theta),
19585 y: center.y + _r2 * Math.sin(theta)
19586 };
19587 pos[_val.node.id()] = p;
19588 }
19589 } // position the nodes
19590
19591
19592 eles.nodes().layoutPositions(this, options, function (ele) {
19593 var id = ele.id();
19594 return pos[id];
19595 });
19596 return this; // chaining
19597};
19598
19599/*
19600The CoSE layout was written by Gerardo Huck.
19601https://www.linkedin.com/in/gerardohuck/
19602
19603Based on the following article:
19604http://dl.acm.org/citation.cfm?id=1498047
19605
19606Modifications tracked on Github.
19607*/
19608var DEBUG;
19609/**
19610 * @brief : default layout options
19611 */
19612
19613var defaults$c = {
19614 // Called on `layoutready`
19615 ready: function ready() {},
19616 // Called on `layoutstop`
19617 stop: function stop() {},
19618 // Whether to animate while running the layout
19619 // true : Animate continuously as the layout is running
19620 // false : Just show the end result
19621 // 'end' : Animate with the end result, from the initial positions to the end positions
19622 animate: true,
19623 // Easing of the animation for animate:'end'
19624 animationEasing: undefined,
19625 // The duration of the animation for animate:'end'
19626 animationDuration: undefined,
19627 // A function that determines whether the node should be animated
19628 // All nodes animated by default on animate enabled
19629 // Non-animated nodes are positioned immediately when the layout starts
19630 animateFilter: function animateFilter(node, i) {
19631 return true;
19632 },
19633 // The layout animates only after this many milliseconds for animate:true
19634 // (prevents flashing on fast runs)
19635 animationThreshold: 250,
19636 // Number of iterations between consecutive screen positions update
19637 refresh: 20,
19638 // Whether to fit the network view after when done
19639 fit: true,
19640 // Padding on fit
19641 padding: 30,
19642 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19643 boundingBox: undefined,
19644 // Excludes the label when calculating node bounding boxes for the layout algorithm
19645 nodeDimensionsIncludeLabels: false,
19646 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19647 randomize: false,
19648 // Extra spacing between components in non-compound graphs
19649 componentSpacing: 40,
19650 // Node repulsion (non overlapping) multiplier
19651 nodeRepulsion: function nodeRepulsion(node) {
19652 return 2048;
19653 },
19654 // Node repulsion (overlapping) multiplier
19655 nodeOverlap: 4,
19656 // Ideal edge (non nested) length
19657 idealEdgeLength: function idealEdgeLength(edge) {
19658 return 32;
19659 },
19660 // Divisor to compute edge forces
19661 edgeElasticity: function edgeElasticity(edge) {
19662 return 32;
19663 },
19664 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19665 nestingFactor: 1.2,
19666 // Gravity force (constant)
19667 gravity: 1,
19668 // Maximum number of iterations to perform
19669 numIter: 1000,
19670 // Initial temperature (maximum node displacement)
19671 initialTemp: 1000,
19672 // Cooling factor (how the temperature is reduced between consecutive iterations
19673 coolingFactor: 0.99,
19674 // Lower temperature threshold (below this point the layout will end)
19675 minTemp: 1.0
19676};
19677/**
19678 * @brief : constructor
19679 * @arg options : object containing layout options
19680 */
19681
19682function CoseLayout(options) {
19683 this.options = extend({}, defaults$c, options);
19684 this.options.layout = this;
19685}
19686/**
19687 * @brief : runs the layout
19688 */
19689
19690
19691CoseLayout.prototype.run = function () {
19692 var options = this.options;
19693 var cy = options.cy;
19694 var layout = this;
19695 layout.stopped = false;
19696
19697 if (options.animate === true || options.animate === false) {
19698 layout.emit({
19699 type: 'layoutstart',
19700 layout: layout
19701 });
19702 } // Set DEBUG - Global variable
19703
19704
19705 if (true === options.debug) {
19706 DEBUG = true;
19707 } else {
19708 DEBUG = false;
19709 } // Initialize layout info
19710
19711
19712 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
19713
19714 if (DEBUG) {
19715 printLayoutInfo(layoutInfo);
19716 } // If required, randomize node positions
19717
19718
19719 if (options.randomize) {
19720 randomizePositions(layoutInfo);
19721 }
19722
19723 var startTime = performanceNow();
19724
19725 var refresh = function refresh() {
19726 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
19727
19728 if (true === options.fit) {
19729 cy.fit(options.padding);
19730 }
19731 };
19732
19733 var mainLoop = function mainLoop(i) {
19734 if (layout.stopped || i >= options.numIter) {
19735 // logDebug("Layout manually stopped. Stopping computation in step " + i);
19736 return false;
19737 } // Do one step in the phisical simulation
19738
19739
19740 step$1(layoutInfo, options); // Update temperature
19741
19742 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
19743
19744 if (layoutInfo.temperature < options.minTemp) {
19745 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
19746 return false;
19747 }
19748
19749 return true;
19750 };
19751
19752 var done = function done() {
19753 if (options.animate === true || options.animate === false) {
19754 refresh(); // Layout has finished
19755
19756 layout.one('layoutstop', options.stop);
19757 layout.emit({
19758 type: 'layoutstop',
19759 layout: layout
19760 });
19761 } else {
19762 var nodes = options.eles.nodes();
19763 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19764 nodes.layoutPositions(layout, options, getScaledPos);
19765 }
19766 };
19767
19768 var i = 0;
19769 var loopRet = true;
19770
19771 if (options.animate === true) {
19772 var frame = function frame() {
19773 var f = 0;
19774
19775 while (loopRet && f < options.refresh) {
19776 loopRet = mainLoop(i);
19777 i++;
19778 f++;
19779 }
19780
19781 if (!loopRet) {
19782 // it's done
19783 separateComponents(layoutInfo, options);
19784 done();
19785 } else {
19786 var now = performanceNow();
19787
19788 if (now - startTime >= options.animationThreshold) {
19789 refresh();
19790 }
19791
19792 requestAnimationFrame(frame);
19793 }
19794 };
19795
19796 frame();
19797 } else {
19798 while (loopRet) {
19799 loopRet = mainLoop(i);
19800 i++;
19801 }
19802
19803 separateComponents(layoutInfo, options);
19804 done();
19805 }
19806
19807 return this; // chaining
19808};
19809/**
19810 * @brief : called on continuous layouts to stop them before they finish
19811 */
19812
19813
19814CoseLayout.prototype.stop = function () {
19815 this.stopped = true;
19816
19817 if (this.thread) {
19818 this.thread.stop();
19819 }
19820
19821 this.emit('layoutstop');
19822 return this; // chaining
19823};
19824
19825CoseLayout.prototype.destroy = function () {
19826 if (this.thread) {
19827 this.thread.stop();
19828 }
19829
19830 return this; // chaining
19831};
19832/**
19833 * @brief : Creates an object which is contains all the data
19834 * used in the layout process
19835 * @arg cy : cytoscape.js object
19836 * @return : layoutInfo object initialized
19837 */
19838
19839
19840var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
19841 // Shortcut
19842 var edges = options.eles.edges();
19843 var nodes = options.eles.nodes();
19844 var layoutInfo = {
19845 isCompound: cy.hasCompoundNodes(),
19846 layoutNodes: [],
19847 idToIndex: {},
19848 nodeSize: nodes.size(),
19849 graphSet: [],
19850 indexToGraph: [],
19851 layoutEdges: [],
19852 edgeSize: edges.size(),
19853 temperature: options.initialTemp,
19854 clientWidth: cy.width(),
19855 clientHeight: cy.width(),
19856 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
19857 x1: 0,
19858 y1: 0,
19859 w: cy.width(),
19860 h: cy.height()
19861 })
19862 };
19863 var components = options.eles.components();
19864 var id2cmptId = {};
19865
19866 for (var i = 0; i < components.length; i++) {
19867 var component = components[i];
19868
19869 for (var j = 0; j < component.length; j++) {
19870 var node = component[j];
19871 id2cmptId[node.id()] = i;
19872 }
19873 } // Iterate over all nodes, creating layout nodes
19874
19875
19876 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19877 var n = nodes[i];
19878 var nbb = n.layoutDimensions(options);
19879 var tempNode = {};
19880 tempNode.isLocked = n.locked();
19881 tempNode.id = n.data('id');
19882 tempNode.parentId = n.data('parent');
19883 tempNode.cmptId = id2cmptId[n.id()];
19884 tempNode.children = [];
19885 tempNode.positionX = n.position('x');
19886 tempNode.positionY = n.position('y');
19887 tempNode.offsetX = 0;
19888 tempNode.offsetY = 0;
19889 tempNode.height = nbb.w;
19890 tempNode.width = nbb.h;
19891 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
19892 tempNode.minX = tempNode.positionX - tempNode.width / 2;
19893 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
19894 tempNode.minY = tempNode.positionY - tempNode.height / 2;
19895 tempNode.padLeft = parseFloat(n.style('padding'));
19896 tempNode.padRight = parseFloat(n.style('padding'));
19897 tempNode.padTop = parseFloat(n.style('padding'));
19898 tempNode.padBottom = parseFloat(n.style('padding')); // forces
19899
19900 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
19901
19902 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
19903
19904 layoutInfo.idToIndex[tempNode.id] = i;
19905 } // Inline implementation of a queue, used for traversing the graph in BFS order
19906
19907
19908 var queue = [];
19909 var start = 0; // Points to the start the queue
19910
19911 var end = -1; // Points to the end of the queue
19912
19913 var tempGraph = []; // Second pass to add child information and
19914 // initialize queue for hierarchical traversal
19915
19916 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19917 var n = layoutInfo.layoutNodes[i];
19918 var p_id = n.parentId; // Check if node n has a parent node
19919
19920 if (null != p_id) {
19921 // Add node Id to parent's list of children
19922 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
19923 } else {
19924 // If a node doesn't have a parent, then it's in the root graph
19925 queue[++end] = n.id;
19926 tempGraph.push(n.id);
19927 }
19928 } // Add root graph to graphSet
19929
19930
19931 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
19932
19933 while (start <= end) {
19934 // Get the node to visit and remove it from queue
19935 var node_id = queue[start++];
19936 var node_ix = layoutInfo.idToIndex[node_id];
19937 var node = layoutInfo.layoutNodes[node_ix];
19938 var children = node.children;
19939
19940 if (children.length > 0) {
19941 // Add children nodes as a new graph to graph set
19942 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
19943
19944 for (var i = 0; i < children.length; i++) {
19945 queue[++end] = children[i];
19946 }
19947 }
19948 } // Create indexToGraph map
19949
19950
19951 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
19952 var graph = layoutInfo.graphSet[i];
19953
19954 for (var j = 0; j < graph.length; j++) {
19955 var index = layoutInfo.idToIndex[graph[j]];
19956 layoutInfo.indexToGraph[index] = i;
19957 }
19958 } // Iterate over all edges, creating Layout Edges
19959
19960
19961 for (var i = 0; i < layoutInfo.edgeSize; i++) {
19962 var e = edges[i];
19963 var tempEdge = {};
19964 tempEdge.id = e.data('id');
19965 tempEdge.sourceId = e.data('source');
19966 tempEdge.targetId = e.data('target'); // Compute ideal length
19967
19968 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
19969 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
19970
19971 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
19972 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
19973 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
19974 var targetGraph = layoutInfo.indexToGraph[targetIx];
19975
19976 if (sourceGraph != targetGraph) {
19977 // Find lowest common graph ancestor
19978 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
19979
19980 var lcaGraph = layoutInfo.graphSet[lca];
19981 var depth = 0; // Source depth
19982
19983 var tempNode = layoutInfo.layoutNodes[sourceIx];
19984
19985 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19986 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19987 depth++;
19988 } // Target depth
19989
19990
19991 tempNode = layoutInfo.layoutNodes[targetIx];
19992
19993 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19994 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19995 depth++;
19996 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
19997 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
19998 // ". Depth: " + depth);
19999 // Update idealLength
20000
20001
20002 idealLength *= depth * options.nestingFactor;
20003 }
20004
20005 tempEdge.idealLength = idealLength;
20006 tempEdge.elasticity = elasticity;
20007 layoutInfo.layoutEdges.push(tempEdge);
20008 } // Finally, return layoutInfo object
20009
20010
20011 return layoutInfo;
20012};
20013/**
20014 * @brief : This function finds the index of the lowest common
20015 * graph ancestor between 2 nodes in the subtree
20016 * (from the graph hierarchy induced tree) whose
20017 * root is graphIx
20018 *
20019 * @arg node1: node1's ID
20020 * @arg node2: node2's ID
20021 * @arg layoutInfo: layoutInfo object
20022 *
20023 */
20024
20025
20026var findLCA = function findLCA(node1, node2, layoutInfo) {
20027 // Find their common ancester, starting from the root graph
20028 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20029
20030 if (2 > res.count) {
20031 // If aux function couldn't find the common ancester,
20032 // then it is the root graph
20033 return 0;
20034 } else {
20035 return res.graph;
20036 }
20037};
20038/**
20039 * @brief : Auxiliary function used for LCA computation
20040 *
20041 * @arg node1 : node1's ID
20042 * @arg node2 : node2's ID
20043 * @arg graphIx : subgraph index
20044 * @arg layoutInfo : layoutInfo object
20045 *
20046 * @return : object of the form {count: X, graph: Y}, where:
20047 * X is the number of ancesters (max: 2) found in
20048 * graphIx (and it's subgraphs),
20049 * Y is the graph index of the lowest graph containing
20050 * all X nodes
20051 */
20052
20053
20054var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20055 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20056
20057 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20058 return {
20059 count: 2,
20060 graph: graphIx
20061 };
20062 } // Make recursive calls for all subgraphs
20063
20064
20065 var c = 0;
20066
20067 for (var i = 0; i < graph.length; i++) {
20068 var nodeId = graph[i];
20069 var nodeIx = layoutInfo.idToIndex[nodeId];
20070 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20071
20072 if (0 === children.length) {
20073 continue;
20074 }
20075
20076 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20077 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20078
20079 if (0 === result.count) {
20080 // Neither node1 nor node2 are present in this subgraph
20081 continue;
20082 } else if (1 === result.count) {
20083 // One of (node1, node2) is present in this subgraph
20084 c++;
20085
20086 if (2 === c) {
20087 // We've already found both nodes, no need to keep searching
20088 break;
20089 }
20090 } else {
20091 // Both nodes are present in this subgraph
20092 return result;
20093 }
20094 }
20095
20096 return {
20097 count: c,
20098 graph: graphIx
20099 };
20100};
20101/**
20102 * @brief: printsLayoutInfo into js console
20103 * Only used for debbuging
20104 */
20105
20106
20107if (false) {
20108 var printLayoutInfo;
20109}
20110/**
20111 * @brief : Randomizes the position of all nodes
20112 */
20113
20114
20115var randomizePositions = function randomizePositions(layoutInfo, cy) {
20116 var width = layoutInfo.clientWidth;
20117 var height = layoutInfo.clientHeight;
20118
20119 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20120 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20121
20122 if (0 === n.children.length && !n.isLocked) {
20123 n.positionX = Math.random() * width;
20124 n.positionY = Math.random() * height;
20125 }
20126 }
20127};
20128
20129var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20130 var bb = layoutInfo.boundingBox;
20131 var coseBB = {
20132 x1: Infinity,
20133 x2: -Infinity,
20134 y1: Infinity,
20135 y2: -Infinity
20136 };
20137
20138 if (options.boundingBox) {
20139 nodes.forEach(function (node) {
20140 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20141 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20142 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20143 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20144 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20145 });
20146 coseBB.w = coseBB.x2 - coseBB.x1;
20147 coseBB.h = coseBB.y2 - coseBB.y1;
20148 }
20149
20150 return function (ele, i) {
20151 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20152
20153 if (options.boundingBox) {
20154 // then add extra bounding box constraint
20155 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20156 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20157 return {
20158 x: bb.x1 + pctX * bb.w,
20159 y: bb.y1 + pctY * bb.h
20160 };
20161 } else {
20162 return {
20163 x: lnode.positionX,
20164 y: lnode.positionY
20165 };
20166 }
20167 };
20168};
20169/**
20170 * @brief : Updates the positions of nodes in the network
20171 * @arg layoutInfo : LayoutInfo object
20172 * @arg cy : Cytoscape object
20173 * @arg options : Layout options
20174 */
20175
20176
20177var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20178 // var s = 'Refreshing positions';
20179 // logDebug(s);
20180 var layout = options.layout;
20181 var nodes = options.eles.nodes();
20182 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20183 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20184
20185 if (true !== layoutInfo.ready) {
20186 // s = 'Triggering layoutready';
20187 // logDebug(s);
20188 layoutInfo.ready = true;
20189 layout.one('layoutready', options.ready);
20190 layout.emit({
20191 type: 'layoutready',
20192 layout: this
20193 });
20194 }
20195};
20196/**
20197 * @brief : Logs a debug message in JS console, if DEBUG is ON
20198 */
20199// var logDebug = function(text) {
20200// if (DEBUG) {
20201// console.debug(text);
20202// }
20203// };
20204
20205/**
20206 * @brief : Performs one iteration of the physical simulation
20207 * @arg layoutInfo : LayoutInfo object already initialized
20208 * @arg cy : Cytoscape object
20209 * @arg options : Layout options
20210 */
20211
20212
20213var step$1 = function step(layoutInfo, options, _step) {
20214 // var s = "\n\n###############################";
20215 // s += "\nSTEP: " + step;
20216 // s += "\n###############################\n";
20217 // logDebug(s);
20218 // Calculate node repulsions
20219 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20220
20221 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20222
20223 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20224
20225 propagateForces(layoutInfo); // Update positions based on calculated forces
20226
20227 updatePositions(layoutInfo);
20228};
20229/**
20230 * @brief : Computes the node repulsion forces
20231 */
20232
20233
20234var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20235 // Go through each of the graphs in graphSet
20236 // Nodes only repel each other if they belong to the same graph
20237 // var s = 'calculateNodeForces';
20238 // logDebug(s);
20239 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20240 var graph = layoutInfo.graphSet[i];
20241 var numNodes = graph.length; // s = "Set: " + graph.toString();
20242 // logDebug(s);
20243 // Now get all the pairs of nodes
20244 // Only get each pair once, (A, B) = (B, A)
20245
20246 for (var j = 0; j < numNodes; j++) {
20247 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20248
20249 for (var k = j + 1; k < numNodes; k++) {
20250 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20251 nodeRepulsion(node1, node2, layoutInfo, options);
20252 }
20253 }
20254 }
20255};
20256
20257var randomDistance = function randomDistance(max) {
20258 return -max + 2 * max * Math.random();
20259};
20260/**
20261 * @brief : Compute the node repulsion forces between a pair of nodes
20262 */
20263
20264
20265var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20266 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20267 var cmptId1 = node1.cmptId;
20268 var cmptId2 = node2.cmptId;
20269
20270 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20271 return;
20272 } // Get direction of line connecting both node centers
20273
20274
20275 var directionX = node2.positionX - node1.positionX;
20276 var directionY = node2.positionY - node1.positionY;
20277 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20278 // If both centers are the same, apply a random force
20279
20280 if (0 === directionX && 0 === directionY) {
20281 directionX = randomDistance(maxRandDist);
20282 directionY = randomDistance(maxRandDist);
20283 }
20284
20285 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20286
20287 if (overlap > 0) {
20288 // s += "\nNodes DO overlap.";
20289 // s += "\nOverlap: " + overlap;
20290 // If nodes overlap, repulsion force is proportional
20291 // to the overlap
20292 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
20293
20294 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
20295
20296 var forceX = force * directionX / distance;
20297 var forceY = force * directionY / distance;
20298 } else {
20299 // s += "\nNodes do NOT overlap.";
20300 // If there's no overlap, force is inversely proportional
20301 // to squared distance
20302 // Get clipping points for both nodes
20303 var point1 = findClippingPoint(node1, directionX, directionY);
20304 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
20305
20306 var distanceX = point2.x - point1.x;
20307 var distanceY = point2.y - point1.y;
20308 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20309 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
20310 // Compute the module and components of the force vector
20311
20312 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20313 var forceX = force * distanceX / distance;
20314 var forceY = force * distanceY / distance;
20315 } // Apply force
20316
20317
20318 if (!node1.isLocked) {
20319 node1.offsetX -= forceX;
20320 node1.offsetY -= forceY;
20321 }
20322
20323 if (!node2.isLocked) {
20324 node2.offsetX += forceX;
20325 node2.offsetY += forceY;
20326 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20327 // logDebug(s);
20328
20329
20330 return;
20331};
20332/**
20333 * @brief : Determines whether two nodes overlap or not
20334 * @return : Amount of overlapping (0 => no overlap)
20335 */
20336
20337
20338var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20339 if (dX > 0) {
20340 var overlapX = node1.maxX - node2.minX;
20341 } else {
20342 var overlapX = node2.maxX - node1.minX;
20343 }
20344
20345 if (dY > 0) {
20346 var overlapY = node1.maxY - node2.minY;
20347 } else {
20348 var overlapY = node2.maxY - node1.minY;
20349 }
20350
20351 if (overlapX >= 0 && overlapY >= 0) {
20352 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20353 } else {
20354 return 0;
20355 }
20356};
20357/**
20358 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20359 * the rectangular bounding box of it's source/target node
20360 */
20361
20362
20363var findClippingPoint = function findClippingPoint(node, dX, dY) {
20364 // Shorcuts
20365 var X = node.positionX;
20366 var Y = node.positionY;
20367 var H = node.height || 1;
20368 var W = node.width || 1;
20369 var dirSlope = dY / dX;
20370 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
20371 // " . Height: " + H + ", Width: " + W +
20372 // "\nDirection " + dX + ", " + dY;
20373 //
20374 // Compute intersection
20375
20376 var res = {}; // Case: Vertical direction (up)
20377
20378 if (0 === dX && 0 < dY) {
20379 res.x = X; // s += "\nUp direction";
20380
20381 res.y = Y + H / 2;
20382 return res;
20383 } // Case: Vertical direction (down)
20384
20385
20386 if (0 === dX && 0 > dY) {
20387 res.x = X;
20388 res.y = Y + H / 2; // s += "\nDown direction";
20389
20390 return res;
20391 } // Case: Intersects the right border
20392
20393
20394 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20395 res.x = X + W / 2;
20396 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
20397
20398 return res;
20399 } // Case: Intersects the left border
20400
20401
20402 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20403 res.x = X - W / 2;
20404 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
20405
20406 return res;
20407 } // Case: Intersects the top border
20408
20409
20410 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20411 res.x = X + H * dX / 2 / dY;
20412 res.y = Y + H / 2; // s += "\nTop border";
20413
20414 return res;
20415 } // Case: Intersects the bottom border
20416
20417
20418 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20419 res.x = X - H * dX / 2 / dY;
20420 res.y = Y - H / 2; // s += "\nBottom border";
20421
20422 return res;
20423 } // s += "\nClipping point found at " + res.x + ", " + res.y;
20424 // logDebug(s);
20425
20426
20427 return res;
20428};
20429/**
20430 * @brief : Calculates all edge forces
20431 */
20432
20433
20434var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20435 // Iterate over all edges
20436 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20437 // Get edge, source & target nodes
20438 var edge = layoutInfo.layoutEdges[i];
20439 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20440 var source = layoutInfo.layoutNodes[sourceIx];
20441 var targetIx = layoutInfo.idToIndex[edge.targetId];
20442 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
20443
20444 var directionX = target.positionX - source.positionX;
20445 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
20446 // A random force has already been applied as node repulsion
20447
20448 if (0 === directionX && 0 === directionY) {
20449 continue;
20450 } // Get clipping points for both nodes
20451
20452
20453 var point1 = findClippingPoint(source, directionX, directionY);
20454 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20455 var lx = point2.x - point1.x;
20456 var ly = point2.y - point1.y;
20457 var l = Math.sqrt(lx * lx + ly * ly);
20458 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20459
20460 if (0 !== l) {
20461 var forceX = force * lx / l;
20462 var forceY = force * ly / l;
20463 } else {
20464 var forceX = 0;
20465 var forceY = 0;
20466 } // Add this force to target and source nodes
20467
20468
20469 if (!source.isLocked) {
20470 source.offsetX += forceX;
20471 source.offsetY += forceY;
20472 }
20473
20474 if (!target.isLocked) {
20475 target.offsetX -= forceX;
20476 target.offsetY -= forceY;
20477 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20478 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20479 // logDebug(s);
20480
20481 }
20482};
20483/**
20484 * @brief : Computes gravity forces for all nodes
20485 */
20486
20487
20488var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20489 var distThreshold = 1; // var s = 'calculateGravityForces';
20490 // logDebug(s);
20491
20492 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20493 var graph = layoutInfo.graphSet[i];
20494 var numNodes = graph.length; // s = "Set: " + graph.toString();
20495 // logDebug(s);
20496 // Compute graph center
20497
20498 if (0 === i) {
20499 var centerX = layoutInfo.clientHeight / 2;
20500 var centerY = layoutInfo.clientWidth / 2;
20501 } else {
20502 // Get Parent node for this graph, and use its position as center
20503 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20504 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20505 var centerX = parent.positionX;
20506 var centerY = parent.positionY;
20507 } // s = "Center found at: " + centerX + ", " + centerY;
20508 // logDebug(s);
20509 // Apply force to all nodes in graph
20510
20511
20512 for (var j = 0; j < numNodes; j++) {
20513 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
20514
20515 if (node.isLocked) {
20516 continue;
20517 }
20518
20519 var dx = centerX - node.positionX;
20520 var dy = centerY - node.positionY;
20521 var d = Math.sqrt(dx * dx + dy * dy);
20522
20523 if (d > distThreshold) {
20524 var fx = options.gravity * dx / d;
20525 var fy = options.gravity * dy / d;
20526 node.offsetX += fx;
20527 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
20528 } // s += ": skypped since it's too close to center";
20529 // logDebug(s);
20530
20531 }
20532 }
20533};
20534/**
20535 * @brief : This function propagates the existing offsets from
20536 * parent nodes to its descendents.
20537 * @arg layoutInfo : layoutInfo Object
20538 * @arg cy : cytoscape Object
20539 * @arg options : Layout options
20540 */
20541
20542
20543var propagateForces = function propagateForces(layoutInfo, options) {
20544 // Inline implementation of a queue, used for traversing the graph in BFS order
20545 var queue = [];
20546 var start = 0; // Points to the start the queue
20547
20548 var end = -1; // Points to the end of the queue
20549 // logDebug('propagateForces');
20550 // Start by visiting the nodes in the root graph
20551
20552 queue.push.apply(queue, layoutInfo.graphSet[0]);
20553 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
20554
20555 while (start <= end) {
20556 // Get the node to visit and remove it from queue
20557 var nodeId = queue[start++];
20558 var nodeIndex = layoutInfo.idToIndex[nodeId];
20559 var node = layoutInfo.layoutNodes[nodeIndex];
20560 var children = node.children; // We only need to process the node if it's compound
20561
20562 if (0 < children.length && !node.isLocked) {
20563 var offX = node.offsetX;
20564 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
20565 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20566 // s += "\n Children: " + children.toString();
20567 // logDebug(s);
20568
20569 for (var i = 0; i < children.length; i++) {
20570 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
20571
20572 childNode.offsetX += offX;
20573 childNode.offsetY += offY; // Add children to queue to be visited
20574
20575 queue[++end] = children[i];
20576 } // Reset parent offsets
20577
20578
20579 node.offsetX = 0;
20580 node.offsetY = 0;
20581 }
20582 }
20583};
20584/**
20585 * @brief : Updates the layout model positions, based on
20586 * the accumulated forces
20587 */
20588
20589
20590var updatePositions = function updatePositions(layoutInfo, options) {
20591 // var s = 'Updating positions';
20592 // logDebug(s);
20593 // Reset boundaries for compound nodes
20594 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20595 var n = layoutInfo.layoutNodes[i];
20596
20597 if (0 < n.children.length) {
20598 // logDebug("Resetting boundaries of compound node: " + n.id);
20599 n.maxX = undefined;
20600 n.minX = undefined;
20601 n.maxY = undefined;
20602 n.minY = undefined;
20603 }
20604 }
20605
20606 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20607 var n = layoutInfo.layoutNodes[i];
20608
20609 if (0 < n.children.length || n.isLocked) {
20610 // No need to set compound or locked node position
20611 // logDebug("Skipping position update of node: " + n.id);
20612 continue;
20613 } // s = "Node: " + n.id + " Previous position: (" +
20614 // n.positionX + ", " + n.positionY + ").";
20615 // Limit displacement in order to improve stability
20616
20617
20618 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20619 n.positionX += tempForce.x;
20620 n.positionY += tempForce.y;
20621 n.offsetX = 0;
20622 n.offsetY = 0;
20623 n.minX = n.positionX - n.width;
20624 n.maxX = n.positionX + n.width;
20625 n.minY = n.positionY - n.height;
20626 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20627 // logDebug(s);
20628 // Update ancestry boudaries
20629
20630 updateAncestryBoundaries(n, layoutInfo);
20631 } // Update size, position of compund nodes
20632
20633
20634 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20635 var n = layoutInfo.layoutNodes[i];
20636
20637 if (0 < n.children.length && !n.isLocked) {
20638 n.positionX = (n.maxX + n.minX) / 2;
20639 n.positionY = (n.maxY + n.minY) / 2;
20640 n.width = n.maxX - n.minX;
20641 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
20642 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20643 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20644 // logDebug(s);
20645 }
20646 }
20647};
20648/**
20649 * @brief : Limits a force (forceX, forceY) to be not
20650 * greater (in modulo) than max.
20651 8 Preserves force direction.
20652 */
20653
20654
20655var limitForce = function limitForce(forceX, forceY, max) {
20656 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20657 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20658
20659 if (force > max) {
20660 var res = {
20661 x: max * forceX / force,
20662 y: max * forceY / force
20663 };
20664 } else {
20665 var res = {
20666 x: forceX,
20667 y: forceY
20668 };
20669 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20670 // logDebug(s);
20671
20672
20673 return res;
20674};
20675/**
20676 * @brief : Function used for keeping track of compound node
20677 * sizes, since they should bound all their subnodes.
20678 */
20679
20680
20681var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20682 // var s = "Propagating new position/size of node " + node.id;
20683 var parentId = node.parentId;
20684
20685 if (null == parentId) {
20686 // If there's no parent, we are done
20687 // s += ". No parent node.";
20688 // logDebug(s);
20689 return;
20690 } // Get Parent Node
20691
20692
20693 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20694 var flag = false; // MaxX
20695
20696 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20697 p.maxX = node.maxX + p.padRight;
20698 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20699 } // MinX
20700
20701
20702 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20703 p.minX = node.minX - p.padLeft;
20704 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20705 } // MaxY
20706
20707
20708 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20709 p.maxY = node.maxY + p.padBottom;
20710 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20711 } // MinY
20712
20713
20714 if (null == p.minY || node.minY - p.padTop < p.minY) {
20715 p.minY = node.minY - p.padTop;
20716 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20717 } // If updated boundaries, propagate changes upward
20718
20719
20720 if (flag) {
20721 // logDebug(s);
20722 return updateAncestryBoundaries(p, layoutInfo);
20723 } // s += ". No changes in boundaries/position of parent node " + p.id;
20724 // logDebug(s);
20725
20726
20727 return;
20728};
20729
20730var separateComponents = function separateComponents(layoutInfo, options) {
20731 var nodes = layoutInfo.layoutNodes;
20732 var components = [];
20733
20734 for (var i = 0; i < nodes.length; i++) {
20735 var node = nodes[i];
20736 var cid = node.cmptId;
20737 var component = components[cid] = components[cid] || [];
20738 component.push(node);
20739 }
20740
20741 var totalA = 0;
20742
20743 for (var i = 0; i < components.length; i++) {
20744 var c = components[i];
20745
20746 if (!c) {
20747 continue;
20748 }
20749
20750 c.x1 = Infinity;
20751 c.x2 = -Infinity;
20752 c.y1 = Infinity;
20753 c.y2 = -Infinity;
20754
20755 for (var j = 0; j < c.length; j++) {
20756 var n = c[j];
20757 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
20758 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
20759 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
20760 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
20761 }
20762
20763 c.w = c.x2 - c.x1;
20764 c.h = c.y2 - c.y1;
20765 totalA += c.w * c.h;
20766 }
20767
20768 components.sort(function (c1, c2) {
20769 return c2.w * c2.h - c1.w * c1.h;
20770 });
20771 var x = 0;
20772 var y = 0;
20773 var usedW = 0;
20774 var rowH = 0;
20775 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
20776
20777 for (var i = 0; i < components.length; i++) {
20778 var c = components[i];
20779
20780 if (!c) {
20781 continue;
20782 }
20783
20784 for (var j = 0; j < c.length; j++) {
20785 var n = c[j];
20786
20787 if (!n.isLocked) {
20788 n.positionX += x - c.x1;
20789 n.positionY += y - c.y1;
20790 }
20791 }
20792
20793 x += c.w + options.componentSpacing;
20794 usedW += c.w + options.componentSpacing;
20795 rowH = Math.max(rowH, c.h);
20796
20797 if (usedW > maxRowW) {
20798 y += rowH + options.componentSpacing;
20799 x = 0;
20800 usedW = 0;
20801 rowH = 0;
20802 }
20803 }
20804};
20805
20806var defaults$d = {
20807 fit: true,
20808 // whether to fit the viewport to the graph
20809 padding: 30,
20810 // padding used on fit
20811 boundingBox: undefined,
20812 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20813 avoidOverlap: true,
20814 // prevents node overlap, may overflow boundingBox if not enough space
20815 avoidOverlapPadding: 10,
20816 // extra spacing around nodes when avoidOverlap: true
20817 nodeDimensionsIncludeLabels: false,
20818 // Excludes the label when calculating node bounding boxes for the layout algorithm
20819 spacingFactor: undefined,
20820 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20821 condense: false,
20822 // uses all available space on false, uses minimal space on true
20823 rows: undefined,
20824 // force num of rows in the grid
20825 cols: undefined,
20826 // force num of columns in the grid
20827 position: function position(node) {},
20828 // returns { row, col } for element
20829 sort: undefined,
20830 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20831 animate: false,
20832 // whether to transition the node positions
20833 animationDuration: 500,
20834 // duration of animation in ms if enabled
20835 animationEasing: undefined,
20836 // easing of animation if enabled
20837 animateFilter: function animateFilter(node, i) {
20838 return true;
20839 },
20840 // 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
20841 ready: undefined,
20842 // callback on layoutready
20843 stop: undefined,
20844 // callback on layoutstop
20845 transform: function transform(node, position) {
20846 return position;
20847 } // transform a given node position. Useful for changing flow direction in discrete layouts
20848
20849};
20850
20851function GridLayout(options) {
20852 this.options = extend({}, defaults$d, options);
20853}
20854
20855GridLayout.prototype.run = function () {
20856 var params = this.options;
20857 var options = params;
20858 var cy = params.cy;
20859 var eles = options.eles;
20860 var nodes = eles.nodes().not(':parent');
20861
20862 if (options.sort) {
20863 nodes = nodes.sort(options.sort);
20864 }
20865
20866 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20867 x1: 0,
20868 y1: 0,
20869 w: cy.width(),
20870 h: cy.height()
20871 });
20872
20873 if (bb.h === 0 || bb.w === 0) {
20874 eles.nodes().layoutPositions(this, options, function (ele) {
20875 return {
20876 x: bb.x1,
20877 y: bb.y1
20878 };
20879 });
20880 } else {
20881 // width/height * splits^2 = cells where splits is number of times to split width
20882 var cells = nodes.size();
20883 var splits = Math.sqrt(cells * bb.h / bb.w);
20884 var rows = Math.round(splits);
20885 var cols = Math.round(bb.w / bb.h * splits);
20886
20887 var small = function small(val) {
20888 if (val == null) {
20889 return Math.min(rows, cols);
20890 } else {
20891 var min = Math.min(rows, cols);
20892
20893 if (min == rows) {
20894 rows = val;
20895 } else {
20896 cols = val;
20897 }
20898 }
20899 };
20900
20901 var large = function large(val) {
20902 if (val == null) {
20903 return Math.max(rows, cols);
20904 } else {
20905 var max = Math.max(rows, cols);
20906
20907 if (max == rows) {
20908 rows = val;
20909 } else {
20910 cols = val;
20911 }
20912 }
20913 };
20914
20915 var oRows = options.rows;
20916 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
20917
20918 if (oRows != null && oCols != null) {
20919 rows = oRows;
20920 cols = oCols;
20921 } else if (oRows != null && oCols == null) {
20922 rows = oRows;
20923 cols = Math.ceil(cells / rows);
20924 } else if (oRows == null && oCols != null) {
20925 cols = oCols;
20926 rows = Math.ceil(cells / cols);
20927 } // otherwise use the automatic values and adjust accordingly
20928 // if rounding was up, see if we can reduce rows or columns
20929 else if (cols * rows > cells) {
20930 var sm = small();
20931 var lg = large(); // reducing the small side takes away the most cells, so try it first
20932
20933 if ((sm - 1) * lg >= cells) {
20934 small(sm - 1);
20935 } else if ((lg - 1) * sm >= cells) {
20936 large(lg - 1);
20937 }
20938 } else {
20939 // if rounding was too low, add rows or columns
20940 while (cols * rows < cells) {
20941 var _sm = small();
20942
20943 var _lg = large(); // try to add to larger side first (adds less in multiplication)
20944
20945
20946 if ((_lg + 1) * _sm >= cells) {
20947 large(_lg + 1);
20948 } else {
20949 small(_sm + 1);
20950 }
20951 }
20952 }
20953
20954 var cellWidth = bb.w / cols;
20955 var cellHeight = bb.h / rows;
20956
20957 if (options.condense) {
20958 cellWidth = 0;
20959 cellHeight = 0;
20960 }
20961
20962 if (options.avoidOverlap) {
20963 for (var i = 0; i < nodes.length; i++) {
20964 var node = nodes[i];
20965 var pos = node._private.position;
20966
20967 if (pos.x == null || pos.y == null) {
20968 // for bb
20969 pos.x = 0;
20970 pos.y = 0;
20971 }
20972
20973 var nbb = node.layoutDimensions(options);
20974 var p = options.avoidOverlapPadding;
20975 var w = nbb.w + p;
20976 var h = nbb.h + p;
20977 cellWidth = Math.max(cellWidth, w);
20978 cellHeight = Math.max(cellHeight, h);
20979 }
20980 }
20981
20982 var cellUsed = {}; // e.g. 'c-0-2' => true
20983
20984 var used = function used(row, col) {
20985 return cellUsed['c-' + row + '-' + col] ? true : false;
20986 };
20987
20988 var use = function use(row, col) {
20989 cellUsed['c-' + row + '-' + col] = true;
20990 }; // to keep track of current cell position
20991
20992
20993 var row = 0;
20994 var col = 0;
20995
20996 var moveToNextCell = function moveToNextCell() {
20997 col++;
20998
20999 if (col >= cols) {
21000 col = 0;
21001 row++;
21002 }
21003 }; // get a cache of all the manual positions
21004
21005
21006 var id2manPos = {};
21007
21008 for (var _i = 0; _i < nodes.length; _i++) {
21009 var _node = nodes[_i];
21010 var rcPos = options.position(_node);
21011
21012 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21013 // must have at least row or col def'd
21014 var _pos = {
21015 row: rcPos.row,
21016 col: rcPos.col
21017 };
21018
21019 if (_pos.col === undefined) {
21020 // find unused col
21021 _pos.col = 0;
21022
21023 while (used(_pos.row, _pos.col)) {
21024 _pos.col++;
21025 }
21026 } else if (_pos.row === undefined) {
21027 // find unused row
21028 _pos.row = 0;
21029
21030 while (used(_pos.row, _pos.col)) {
21031 _pos.row++;
21032 }
21033 }
21034
21035 id2manPos[_node.id()] = _pos;
21036 use(_pos.row, _pos.col);
21037 }
21038 }
21039
21040 var getPos = function getPos(element, i) {
21041 var x, y;
21042
21043 if (element.locked() || element.isParent()) {
21044 return false;
21045 } // see if we have a manual position set
21046
21047
21048 var rcPos = id2manPos[element.id()];
21049
21050 if (rcPos) {
21051 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21052 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21053 } else {
21054 // otherwise set automatically
21055 while (used(row, col)) {
21056 moveToNextCell();
21057 }
21058
21059 x = col * cellWidth + cellWidth / 2 + bb.x1;
21060 y = row * cellHeight + cellHeight / 2 + bb.y1;
21061 use(row, col);
21062 moveToNextCell();
21063 }
21064
21065 return {
21066 x: x,
21067 y: y
21068 };
21069 };
21070
21071 nodes.layoutPositions(this, options, getPos);
21072 }
21073
21074 return this; // chaining
21075};
21076
21077var defaults$e = {
21078 ready: function ready() {},
21079 // on layoutready
21080 stop: function stop() {} // on layoutstop
21081
21082}; // constructor
21083// options : object containing layout options
21084
21085function NullLayout(options) {
21086 this.options = extend({}, defaults$e, options);
21087} // runs the layout
21088
21089
21090NullLayout.prototype.run = function () {
21091 var options = this.options;
21092 var eles = options.eles; // elements to consider in the layout
21093
21094 var layout = this; // cy is automatically populated for us in the constructor
21095 // (disable eslint for next line as this serves as example layout code to external developers)
21096 // eslint-disable-next-line no-unused-vars
21097
21098 var cy = options.cy;
21099 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21100 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21101
21102 eles.nodes().positions(function () {
21103 return {
21104 x: 0,
21105 y: 0
21106 };
21107 }); // trigger layoutready when each node has had its position set at least once
21108
21109 layout.one('layoutready', options.ready);
21110 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21111
21112 layout.one('layoutstop', options.stop);
21113 layout.emit('layoutstop');
21114 return this; // chaining
21115}; // called on continuous layouts to stop them before they finish
21116
21117
21118NullLayout.prototype.stop = function () {
21119 return this; // chaining
21120};
21121
21122var defaults$f = {
21123 positions: undefined,
21124 // map of (node id) => (position obj); or function(node){ return somPos; }
21125 zoom: undefined,
21126 // the zoom level to set (prob want fit = false if set)
21127 pan: undefined,
21128 // the pan level to set (prob want fit = false if set)
21129 fit: true,
21130 // whether to fit to viewport
21131 padding: 30,
21132 // padding on fit
21133 animate: false,
21134 // whether to transition the node positions
21135 animationDuration: 500,
21136 // duration of animation in ms if enabled
21137 animationEasing: undefined,
21138 // easing of animation if enabled
21139 animateFilter: function animateFilter(node, i) {
21140 return true;
21141 },
21142 // 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
21143 ready: undefined,
21144 // callback on layoutready
21145 stop: undefined,
21146 // callback on layoutstop
21147 transform: function transform(node, position) {
21148 return position;
21149 } // transform a given node position. Useful for changing flow direction in discrete layouts
21150
21151};
21152
21153function PresetLayout(options) {
21154 this.options = extend({}, defaults$f, options);
21155}
21156
21157PresetLayout.prototype.run = function () {
21158 var options = this.options;
21159 var eles = options.eles;
21160 var nodes = eles.nodes();
21161 var posIsFn = fn(options.positions);
21162
21163 function getPosition(node) {
21164 if (options.positions == null) {
21165 return copyPosition(node.position());
21166 }
21167
21168 if (posIsFn) {
21169 return options.positions(node);
21170 }
21171
21172 var pos = options.positions[node._private.data.id];
21173
21174 if (pos == null) {
21175 return null;
21176 }
21177
21178 return pos;
21179 }
21180
21181 nodes.layoutPositions(this, options, function (node, i) {
21182 var position = getPosition(node);
21183
21184 if (node.locked() || position == null) {
21185 return false;
21186 }
21187
21188 return position;
21189 });
21190 return this; // chaining
21191};
21192
21193var defaults$g = {
21194 fit: true,
21195 // whether to fit to viewport
21196 padding: 30,
21197 // fit padding
21198 boundingBox: undefined,
21199 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21200 animate: false,
21201 // whether to transition the node positions
21202 animationDuration: 500,
21203 // duration of animation in ms if enabled
21204 animationEasing: undefined,
21205 // easing of animation if enabled
21206 animateFilter: function animateFilter(node, i) {
21207 return true;
21208 },
21209 // 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
21210 ready: undefined,
21211 // callback on layoutready
21212 stop: undefined,
21213 // callback on layoutstop
21214 transform: function transform(node, position) {
21215 return position;
21216 } // transform a given node position. Useful for changing flow direction in discrete layouts
21217
21218};
21219
21220function RandomLayout(options) {
21221 this.options = extend({}, defaults$g, options);
21222}
21223
21224RandomLayout.prototype.run = function () {
21225 var options = this.options;
21226 var cy = options.cy;
21227 var eles = options.eles;
21228 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21229 x1: 0,
21230 y1: 0,
21231 w: cy.width(),
21232 h: cy.height()
21233 });
21234
21235 var getPos = function getPos(node, i) {
21236 return {
21237 x: bb.x1 + Math.round(Math.random() * bb.w),
21238 y: bb.y1 + Math.round(Math.random() * bb.h)
21239 };
21240 };
21241
21242 eles.nodes().layoutPositions(this, options, getPos);
21243 return this; // chaining
21244};
21245
21246var layout = [{
21247 name: 'breadthfirst',
21248 impl: BreadthFirstLayout
21249}, {
21250 name: 'circle',
21251 impl: CircleLayout
21252}, {
21253 name: 'concentric',
21254 impl: ConcentricLayout
21255}, {
21256 name: 'cose',
21257 impl: CoseLayout
21258}, {
21259 name: 'grid',
21260 impl: GridLayout
21261}, {
21262 name: 'null',
21263 impl: NullLayout
21264}, {
21265 name: 'preset',
21266 impl: PresetLayout
21267}, {
21268 name: 'random',
21269 impl: RandomLayout
21270}];
21271
21272function NullRenderer(options) {
21273 this.options = options;
21274 this.notifications = 0; // for testing
21275}
21276
21277var noop$1 = function noop() {};
21278
21279var throwImgErr = function throwImgErr() {
21280 throw new Error('A headless instance can not render images');
21281};
21282
21283NullRenderer.prototype = {
21284 recalculateRenderedStyle: noop$1,
21285 notify: function notify() {
21286 this.notifications++;
21287 },
21288 init: noop$1,
21289 isHeadless: function isHeadless() {
21290 return true;
21291 },
21292 png: throwImgErr,
21293 jpg: throwImgErr
21294};
21295
21296var BRp = {};
21297BRp.arrowShapeWidth = 0.3;
21298
21299BRp.registerArrowShapes = function () {
21300 var arrowShapes = this.arrowShapes = {};
21301 var renderer = this; // Contract for arrow shapes:
21302 // 0, 0 is arrow tip
21303 // (0, 1) is direction towards node
21304 // (1, 0) is right
21305 //
21306 // functional api:
21307 // collide: check x, y in shape
21308 // roughCollide: called before collide, no false negatives
21309 // draw: draw
21310 // spacing: dist(arrowTip, nodeBoundary)
21311 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21312
21313 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21314 var x1 = translation.x - size / 2 - padding;
21315 var x2 = translation.x + size / 2 + padding;
21316 var y1 = translation.y - size / 2 - padding;
21317 var y2 = translation.y + size / 2 + padding;
21318 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21319 return inside;
21320 };
21321
21322 var transform = function transform(x, y, size, angle, translation) {
21323 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21324 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21325 var xScaled = xRotated * size;
21326 var yScaled = yRotated * size;
21327 var xTranslated = xScaled + translation.x;
21328 var yTranslated = yScaled + translation.y;
21329 return {
21330 x: xTranslated,
21331 y: yTranslated
21332 };
21333 };
21334
21335 var transformPoints = function transformPoints(pts, size, angle, translation) {
21336 var retPts = [];
21337
21338 for (var i = 0; i < pts.length; i += 2) {
21339 var x = pts[i];
21340 var y = pts[i + 1];
21341 retPts.push(transform(x, y, size, angle, translation));
21342 }
21343
21344 return retPts;
21345 };
21346
21347 var pointsToArr = function pointsToArr(pts) {
21348 var ret = [];
21349
21350 for (var i = 0; i < pts.length; i++) {
21351 var p = pts[i];
21352 ret.push(p.x, p.y);
21353 }
21354
21355 return ret;
21356 };
21357
21358 var standardGap = function standardGap(edge) {
21359 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21360 };
21361
21362 var defineArrowShape = function defineArrowShape(name, defn) {
21363 if (string(defn)) {
21364 defn = arrowShapes[defn];
21365 }
21366
21367 arrowShapes[name] = extend({
21368 name: name,
21369 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21370 collide: function collide(x, y, size, angle, translation, padding) {
21371 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21372 var inside = pointInsidePolygonPoints(x, y, points);
21373 return inside;
21374 },
21375 roughCollide: bbCollide,
21376 draw: function draw(context, size, angle, translation) {
21377 var points = transformPoints(this.points, size, angle, translation);
21378 renderer.arrowShapeImpl('polygon')(context, points);
21379 },
21380 spacing: function spacing(edge) {
21381 return 0;
21382 },
21383 gap: standardGap
21384 }, defn);
21385 };
21386
21387 defineArrowShape('none', {
21388 collide: falsify,
21389 roughCollide: falsify,
21390 draw: noop,
21391 spacing: zeroify,
21392 gap: zeroify
21393 });
21394 defineArrowShape('triangle', {
21395 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21396 });
21397 defineArrowShape('arrow', 'triangle');
21398 defineArrowShape('triangle-backcurve', {
21399 points: arrowShapes['triangle'].points,
21400 controlPoint: [0, -0.15],
21401 roughCollide: bbCollide,
21402 draw: function draw(context, size, angle, translation, edgeWidth) {
21403 var ptsTrans = transformPoints(this.points, size, angle, translation);
21404 var ctrlPt = this.controlPoint;
21405 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21406 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21407 },
21408 gap: function gap(edge) {
21409 return standardGap(edge) * 0.8;
21410 }
21411 });
21412 defineArrowShape('triangle-tee', {
21413 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21414 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21415 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21416 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21417 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21418 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21419 return inside;
21420 },
21421 draw: function draw(context, size, angle, translation, edgeWidth) {
21422 var triPts = transformPoints(this.points, size, angle, translation);
21423 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21424 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21425 }
21426 });
21427 defineArrowShape('circle-triangle', {
21428 radius: 0.15,
21429 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
21430 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21431 var t = translation;
21432 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21433 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21434 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
21435 },
21436 draw: function draw(context, size, angle, translation, edgeWidth) {
21437 var triPts = transformPoints(this.pointsTr, size, angle, translation);
21438 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
21439 },
21440 spacing: function spacing(edge) {
21441 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21442 }
21443 });
21444 defineArrowShape('triangle-cross', {
21445 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21446 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
21447 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
21448 0.15, -0.4],
21449 crossLinePts: function crossLinePts(size, edgeWidth) {
21450 // shift points so that the distance between the cross points matches edge width
21451 var p = this.baseCrossLinePts.slice();
21452 var shiftFactor = edgeWidth / size;
21453 var y0 = 3;
21454 var y1 = 5;
21455 p[y0] = p[y0] - shiftFactor;
21456 p[y1] = p[y1] - shiftFactor;
21457 return p;
21458 },
21459 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21460 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21461 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21462 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21463 return inside;
21464 },
21465 draw: function draw(context, size, angle, translation, edgeWidth) {
21466 var triPts = transformPoints(this.points, size, angle, translation);
21467 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21468 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21469 }
21470 });
21471 defineArrowShape('vee', {
21472 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21473 gap: function gap(edge) {
21474 return standardGap(edge) * 0.525;
21475 }
21476 });
21477 defineArrowShape('circle', {
21478 radius: 0.15,
21479 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21480 var t = translation;
21481 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21482 return inside;
21483 },
21484 draw: function draw(context, size, angle, translation, edgeWidth) {
21485 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21486 },
21487 spacing: function spacing(edge) {
21488 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21489 }
21490 });
21491 defineArrowShape('tee', {
21492 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21493 spacing: function spacing(edge) {
21494 return 1;
21495 },
21496 gap: function gap(edge) {
21497 return 1;
21498 }
21499 });
21500 defineArrowShape('square', {
21501 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21502 });
21503 defineArrowShape('diamond', {
21504 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21505 gap: function gap(edge) {
21506 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21507 }
21508 });
21509 defineArrowShape('chevron', {
21510 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21511 gap: function gap(edge) {
21512 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21513 }
21514 });
21515};
21516
21517var BRp$1 = {}; // Project mouse
21518
21519BRp$1.projectIntoViewport = function (clientX, clientY) {
21520 var cy = this.cy;
21521 var offsets = this.findContainerClientCoords();
21522 var offsetLeft = offsets[0];
21523 var offsetTop = offsets[1];
21524 var scale = offsets[4];
21525 var pan = cy.pan();
21526 var zoom = cy.zoom();
21527 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21528 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21529 return [x, y];
21530};
21531
21532BRp$1.findContainerClientCoords = function () {
21533 if (this.containerBB) {
21534 return this.containerBB;
21535 }
21536
21537 var container = this.container;
21538 var rect = container.getBoundingClientRect();
21539 var style = window$1.getComputedStyle(container);
21540
21541 var styleValue = function styleValue(name) {
21542 return parseFloat(style.getPropertyValue(name));
21543 };
21544
21545 var padding = {
21546 left: styleValue('padding-left'),
21547 right: styleValue('padding-right'),
21548 top: styleValue('padding-top'),
21549 bottom: styleValue('padding-bottom')
21550 };
21551 var border = {
21552 left: styleValue('border-left-width'),
21553 right: styleValue('border-right-width'),
21554 top: styleValue('border-top-width'),
21555 bottom: styleValue('border-bottom-width')
21556 };
21557 var clientWidth = container.clientWidth;
21558 var clientHeight = container.clientHeight;
21559 var paddingHor = padding.left + padding.right;
21560 var paddingVer = padding.top + padding.bottom;
21561 var borderHor = border.left + border.right;
21562 var scale = rect.width / (clientWidth + borderHor);
21563 var unscaledW = clientWidth - paddingHor;
21564 var unscaledH = clientHeight - paddingVer;
21565 var left = rect.left + padding.left + border.left;
21566 var top = rect.top + padding.top + border.top;
21567 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21568};
21569
21570BRp$1.invalidateContainerClientCoordsCache = function () {
21571 this.containerBB = null;
21572};
21573
21574BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21575 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21576};
21577
21578BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21579 var self = this;
21580 var r = this;
21581 var eles = r.getCachedZSortedEles();
21582 var near = []; // 1 node max, 1 edge max
21583
21584 var zoom = r.cy.zoom();
21585 var hasCompounds = r.cy.hasCompoundNodes();
21586 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21587 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21588 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21589 var minSqDist = Infinity;
21590 var nearEdge;
21591 var nearNode;
21592
21593 if (interactiveElementsOnly) {
21594 eles = eles.interactive;
21595 }
21596
21597 function addEle(ele, sqDist) {
21598 if (ele.isNode()) {
21599 if (nearNode) {
21600 return; // can't replace node
21601 } else {
21602 nearNode = ele;
21603 near.push(ele);
21604 }
21605 }
21606
21607 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21608 if (nearEdge) {
21609 // then replace existing edge
21610 // can replace only if same z-index
21611 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) {
21612 for (var i = 0; i < near.length; i++) {
21613 if (near[i].isEdge()) {
21614 near[i] = ele;
21615 nearEdge = ele;
21616 minSqDist = sqDist != null ? sqDist : minSqDist;
21617 break;
21618 }
21619 }
21620 }
21621 } else {
21622 near.push(ele);
21623 nearEdge = ele;
21624 minSqDist = sqDist != null ? sqDist : minSqDist;
21625 }
21626 }
21627 }
21628
21629 function checkNode(node) {
21630 var width = node.outerWidth() + 2 * nodeThreshold;
21631 var height = node.outerHeight() + 2 * nodeThreshold;
21632 var hw = width / 2;
21633 var hh = height / 2;
21634 var pos = node.position();
21635
21636 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21637 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21638 ) {
21639 var shape = r.nodeShapes[self.getNodeShape(node)];
21640
21641 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
21642 addEle(node, 0);
21643 return true;
21644 }
21645 }
21646 }
21647
21648 function checkEdge(edge) {
21649 var _p = edge._private;
21650 var rs = _p.rscratch;
21651 var styleWidth = edge.pstyle('width').pfValue;
21652 var scale = edge.pstyle('arrow-scale').value;
21653 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21654
21655 var widthSq = width * width;
21656 var width2 = width * 2;
21657 var src = _p.source;
21658 var tgt = _p.target;
21659 var sqDist;
21660
21661 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21662 var pts = rs.allpts;
21663
21664 for (var i = 0; i + 3 < pts.length; i += 2) {
21665 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]))) {
21666 addEle(edge, sqDist);
21667 return true;
21668 }
21669 }
21670 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21671 var pts = rs.allpts;
21672
21673 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21674 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]))) {
21675 addEle(edge, sqDist);
21676 return true;
21677 }
21678 }
21679 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
21680
21681
21682 var src = src || _p.source;
21683 var tgt = tgt || _p.target;
21684 var arSize = self.getArrowWidth(styleWidth, scale);
21685 var arrows = [{
21686 name: 'source',
21687 x: rs.arrowStartX,
21688 y: rs.arrowStartY,
21689 angle: rs.srcArrowAngle
21690 }, {
21691 name: 'target',
21692 x: rs.arrowEndX,
21693 y: rs.arrowEndY,
21694 angle: rs.tgtArrowAngle
21695 }, {
21696 name: 'mid-source',
21697 x: rs.midX,
21698 y: rs.midY,
21699 angle: rs.midsrcArrowAngle
21700 }, {
21701 name: 'mid-target',
21702 x: rs.midX,
21703 y: rs.midY,
21704 angle: rs.midtgtArrowAngle
21705 }];
21706
21707 for (var i = 0; i < arrows.length; i++) {
21708 var ar = arrows[i];
21709 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21710 var edgeWidth = edge.pstyle('width').pfValue;
21711
21712 if (shape.roughCollide(x, y, arSize, ar.angle, {
21713 x: ar.x,
21714 y: ar.y
21715 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21716 x: ar.x,
21717 y: ar.y
21718 }, edgeWidth, edgeThreshold)) {
21719 addEle(edge);
21720 return true;
21721 }
21722 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21723
21724
21725 if (hasCompounds && near.length > 0) {
21726 checkNode(src);
21727 checkNode(tgt);
21728 }
21729 }
21730
21731 function preprop(obj, name, pre) {
21732 return getPrefixedProperty(obj, name, pre);
21733 }
21734
21735 function checkLabel(ele, prefix) {
21736 var _p = ele._private;
21737 var th = labelThreshold;
21738 var prefixDash;
21739
21740 if (prefix) {
21741 prefixDash = prefix + '-';
21742 } else {
21743 prefixDash = '';
21744 }
21745
21746 ele.boundingBox();
21747 var bb = _p.labelBounds[prefix || 'main'];
21748 var text = ele.pstyle(prefixDash + 'label').value;
21749 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21750
21751 if (!eventsEnabled || !text) {
21752 return;
21753 }
21754
21755 var lx = preprop(_p.rscratch, 'labelX', prefix);
21756 var ly = preprop(_p.rscratch, 'labelY', prefix);
21757 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21758 var ox = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
21759 var oy = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
21760 var lx1 = bb.x1 - th - ox; // (-ox, -oy) as bb already includes margin
21761
21762 var lx2 = bb.x2 + th - ox; // and rotation is about (lx, ly)
21763
21764 var ly1 = bb.y1 - th - oy;
21765 var ly2 = bb.y2 + th - oy;
21766
21767 if (theta) {
21768 var cos = Math.cos(theta);
21769 var sin = Math.sin(theta);
21770
21771 var rotate = function rotate(x, y) {
21772 x = x - lx;
21773 y = y - ly;
21774 return {
21775 x: x * cos - y * sin + lx,
21776 y: x * sin + y * cos + ly
21777 };
21778 };
21779
21780 var px1y1 = rotate(lx1, ly1);
21781 var px1y2 = rotate(lx1, ly2);
21782 var px2y1 = rotate(lx2, ly1);
21783 var px2y2 = rotate(lx2, ly2);
21784 var points = [// with the margin added after the rotation is applied
21785 px1y1.x + ox, px1y1.y + oy, px2y1.x + ox, px2y1.y + oy, px2y2.x + ox, px2y2.y + oy, px1y2.x + ox, px1y2.y + oy];
21786
21787 if (pointInsidePolygonPoints(x, y, points)) {
21788 addEle(ele);
21789 return true;
21790 }
21791 } else {
21792 // do a cheaper bb check
21793 if (inBoundingBox(bb, x, y)) {
21794 addEle(ele);
21795 return true;
21796 }
21797 }
21798 }
21799
21800 for (var i = eles.length - 1; i >= 0; i--) {
21801 // reverse order for precedence
21802 var ele = eles[i];
21803
21804 if (ele.isNode()) {
21805 checkNode(ele) || checkLabel(ele);
21806 } else {
21807 // then edge
21808 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
21809 }
21810 }
21811
21812 return near;
21813}; // 'Give me everything from this box'
21814
21815
21816BRp$1.getAllInBox = function (x1, y1, x2, y2) {
21817 var eles = this.getCachedZSortedEles().interactive;
21818 var box = [];
21819 var x1c = Math.min(x1, x2);
21820 var x2c = Math.max(x1, x2);
21821 var y1c = Math.min(y1, y2);
21822 var y2c = Math.max(y1, y2);
21823 x1 = x1c;
21824 x2 = x2c;
21825 y1 = y1c;
21826 y2 = y2c;
21827 var boxBb = makeBoundingBox({
21828 x1: x1,
21829 y1: y1,
21830 x2: x2,
21831 y2: y2
21832 });
21833
21834 for (var e = 0; e < eles.length; e++) {
21835 var ele = eles[e];
21836
21837 if (ele.isNode()) {
21838 var node = ele;
21839 var nodeBb = node.boundingBox({
21840 includeNodes: true,
21841 includeEdges: false,
21842 includeLabels: false
21843 });
21844
21845 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
21846 box.push(node);
21847 }
21848 } else {
21849 var edge = ele;
21850 var _p = edge._private;
21851 var rs = _p.rscratch;
21852
21853 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
21854 continue;
21855 }
21856
21857 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
21858 continue;
21859 }
21860
21861 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
21862 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
21863 var allInside = true;
21864
21865 for (var i = 0; i < pts.length; i++) {
21866 if (!pointInBoundingBox(boxBb, pts[i])) {
21867 allInside = false;
21868 break;
21869 }
21870 }
21871
21872 if (allInside) {
21873 box.push(edge);
21874 }
21875 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
21876 box.push(edge);
21877 }
21878 }
21879 }
21880
21881 return box;
21882};
21883
21884var BRp$2 = {};
21885
21886BRp$2.calculateArrowAngles = function (edge) {
21887 var rs = edge._private.rscratch;
21888 var isHaystack = rs.edgeType === 'haystack';
21889 var isBezier = rs.edgeType === 'bezier';
21890 var isMultibezier = rs.edgeType === 'multibezier';
21891 var isSegments = rs.edgeType === 'segments';
21892 var isCompound = rs.edgeType === 'compound';
21893 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
21894
21895 var dispX, dispY;
21896 var startX, startY, endX, endY, midX, midY;
21897
21898 if (isHaystack) {
21899 startX = rs.haystackPts[0];
21900 startY = rs.haystackPts[1];
21901 endX = rs.haystackPts[2];
21902 endY = rs.haystackPts[3];
21903 } else {
21904 startX = rs.arrowStartX;
21905 startY = rs.arrowStartY;
21906 endX = rs.arrowEndX;
21907 endY = rs.arrowEndY;
21908 }
21909
21910 midX = rs.midX;
21911 midY = rs.midY; // source
21912 //
21913
21914 if (isSegments) {
21915 dispX = startX - rs.segpts[0];
21916 dispY = startY - rs.segpts[1];
21917 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21918 var pts = rs.allpts;
21919 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
21920 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
21921 dispX = startX - bX;
21922 dispY = startY - bY;
21923 } else {
21924 dispX = startX - midX;
21925 dispY = startY - midY;
21926 }
21927
21928 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
21929 //
21930
21931 var midX = rs.midX;
21932 var midY = rs.midY;
21933
21934 if (isHaystack) {
21935 midX = (startX + endX) / 2;
21936 midY = (startY + endY) / 2;
21937 }
21938
21939 dispX = endX - startX;
21940 dispY = endY - startY;
21941
21942 if (isSegments) {
21943 var pts = rs.allpts;
21944
21945 if (pts.length / 2 % 2 === 0) {
21946 var i2 = pts.length / 2;
21947 var i1 = i2 - 2;
21948 dispX = pts[i2] - pts[i1];
21949 dispY = pts[i2 + 1] - pts[i1 + 1];
21950 } else {
21951 var i2 = pts.length / 2 - 1;
21952 var i1 = i2 - 2;
21953 var i3 = i2 + 2;
21954 dispX = pts[i2] - pts[i1];
21955 dispY = pts[i2 + 1] - pts[i1 + 1];
21956 }
21957 } else if (isMultibezier || isCompound || isSelf) {
21958 var pts = rs.allpts;
21959 var cpts = rs.ctrlpts;
21960 var bp0x, bp0y;
21961 var bp1x, bp1y;
21962
21963 if (cpts.length / 2 % 2 === 0) {
21964 var p0 = pts.length / 2 - 1; // startpt
21965
21966 var ic = p0 + 2;
21967 var p1 = ic + 2;
21968 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
21969 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
21970 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
21971 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
21972 } else {
21973 var ic = pts.length / 2 - 1; // ctrpt
21974
21975 var p0 = ic - 2; // startpt
21976
21977 var p1 = ic + 2; // endpt
21978
21979 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
21980 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
21981 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
21982 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
21983 }
21984
21985 dispX = bp1x - bp0x;
21986 dispY = bp1y - bp0y;
21987 }
21988
21989 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
21990 rs.midDispX = dispX;
21991 rs.midDispY = dispY; // mid source
21992 //
21993
21994 dispX *= -1;
21995 dispY *= -1;
21996
21997 if (isSegments) {
21998 var pts = rs.allpts;
21999
22000 if (pts.length / 2 % 2 === 0) ; else {
22001 var i2 = pts.length / 2 - 1;
22002 var i3 = i2 + 2;
22003 dispX = -(pts[i3] - pts[i2]);
22004 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22005 }
22006 }
22007
22008 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
22009 //
22010
22011 if (isSegments) {
22012 dispX = endX - rs.segpts[rs.segpts.length - 2];
22013 dispY = endY - rs.segpts[rs.segpts.length - 1];
22014 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22015 var pts = rs.allpts;
22016 var l = pts.length;
22017 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22018 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22019 dispX = endX - bX;
22020 dispY = endY - bY;
22021 } else {
22022 dispX = endX - midX;
22023 dispY = endY - midY;
22024 }
22025
22026 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22027};
22028
22029BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
22030 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22031 var cachedVal = cache[edgeWidth + ', ' + scale];
22032
22033 if (cachedVal) {
22034 return cachedVal;
22035 }
22036
22037 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22038 cache[edgeWidth + ', ' + scale] = cachedVal;
22039 return cachedVal;
22040};
22041
22042var BRp$3 = {};
22043
22044BRp$3.findHaystackPoints = function (edges) {
22045 for (var i = 0; i < edges.length; i++) {
22046 var edge = edges[i];
22047 var _p = edge._private;
22048 var rs = _p.rscratch;
22049
22050 if (!rs.haystack) {
22051 var angle = Math.random() * 2 * Math.PI;
22052 rs.source = {
22053 x: Math.cos(angle),
22054 y: Math.sin(angle)
22055 };
22056 angle = Math.random() * 2 * Math.PI;
22057 rs.target = {
22058 x: Math.cos(angle),
22059 y: Math.sin(angle)
22060 };
22061 }
22062
22063 var src = _p.source;
22064 var tgt = _p.target;
22065 var srcPos = src.position();
22066 var tgtPos = tgt.position();
22067 var srcW = src.width();
22068 var tgtW = tgt.width();
22069 var srcH = src.height();
22070 var tgtH = tgt.height();
22071 var radius = edge.pstyle('haystack-radius').value;
22072 var halfRadius = radius / 2; // b/c have to half width/height
22073
22074 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];
22075 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22076 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22077
22078 rs.edgeType = 'haystack';
22079 rs.haystack = true;
22080 this.storeEdgeProjections(edge);
22081 this.calculateArrowAngles(edge);
22082 this.recalculateEdgeLabelProjections(edge);
22083 this.calculateLabelAngles(edge);
22084 }
22085};
22086
22087BRp$3.findSegmentsPoints = function (edge, pairInfo) {
22088 // Segments (multiple straight lines)
22089 var rs = edge._private.rscratch;
22090 var posPts = pairInfo.posPts,
22091 intersectionPts = pairInfo.intersectionPts,
22092 vectorNormInverse = pairInfo.vectorNormInverse;
22093 var edgeDistances = edge.pstyle('edge-distances').value;
22094 var segmentWs = edge.pstyle('segment-weights');
22095 var segmentDs = edge.pstyle('segment-distances');
22096 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22097 rs.edgeType = 'segments';
22098 rs.segpts = [];
22099
22100 for (var s = 0; s < segmentsN; s++) {
22101 var w = segmentWs.pfValue[s];
22102 var d = segmentDs.pfValue[s];
22103 var w1 = 1 - w;
22104 var w2 = w;
22105 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22106 var adjustedMidpt = {
22107 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22108 y: midptPts.y1 * w1 + midptPts.y2 * w2
22109 };
22110 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22111 }
22112};
22113
22114BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22115 // Self-edge
22116 var rs = edge._private.rscratch;
22117 var dirCounts = pairInfo.dirCounts,
22118 srcPos = pairInfo.srcPos;
22119 var ctrlptDists = edge.pstyle('control-point-distances');
22120 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22121 var loopDir = edge.pstyle('loop-direction').pfValue;
22122 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22123 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22124 rs.edgeType = 'self';
22125 var j = i;
22126 var loopDist = stepSize;
22127
22128 if (edgeIsUnbundled) {
22129 j = 0;
22130 loopDist = ctrlptDist;
22131 }
22132
22133 var loopAngle = loopDir - Math.PI / 2;
22134 var outAngle = loopAngle - loopSwp / 2;
22135 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22136
22137 var dc = String(loopDir + '_' + loopSwp);
22138 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22139 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)];
22140};
22141
22142BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22143 // Compound edge
22144 var rs = edge._private.rscratch;
22145 rs.edgeType = 'compound';
22146 var srcPos = pairInfo.srcPos,
22147 tgtPos = pairInfo.tgtPos,
22148 srcW = pairInfo.srcW,
22149 srcH = pairInfo.srcH,
22150 tgtW = pairInfo.tgtW,
22151 tgtH = pairInfo.tgtH;
22152 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22153 var ctrlptDists = edge.pstyle('control-point-distances');
22154 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22155 var j = i;
22156 var loopDist = stepSize;
22157
22158 if (edgeIsUnbundled) {
22159 j = 0;
22160 loopDist = ctrlptDist;
22161 }
22162
22163 var loopW = 50;
22164 var loopaPos = {
22165 x: srcPos.x - srcW / 2,
22166 y: srcPos.y - srcH / 2
22167 };
22168 var loopbPos = {
22169 x: tgtPos.x - tgtW / 2,
22170 y: tgtPos.y - tgtH / 2
22171 };
22172 var loopPos = {
22173 x: Math.min(loopaPos.x, loopbPos.x),
22174 y: Math.min(loopaPos.y, loopbPos.y)
22175 }; // avoids cases with impossible beziers
22176
22177 var minCompoundStretch = 0.5;
22178 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22179 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22180 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];
22181};
22182
22183BRp$3.findStraightEdgePoints = function (edge) {
22184 // Straight edge within bundle
22185 edge._private.rscratch.edgeType = 'straight';
22186};
22187
22188BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22189 var rs = edge._private.rscratch;
22190 var vectorNormInverse = pairInfo.vectorNormInverse,
22191 posPts = pairInfo.posPts,
22192 intersectionPts = pairInfo.intersectionPts;
22193 var edgeDistances = edge.pstyle('edge-distances').value;
22194 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22195 var ctrlptDists = edge.pstyle('control-point-distances');
22196 var ctrlptWs = edge.pstyle('control-point-weights');
22197 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22198 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22199 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22200
22201 var multi = edgeIsUnbundled;
22202 rs.edgeType = multi ? 'multibezier' : 'bezier';
22203 rs.ctrlpts = [];
22204
22205 for (var b = 0; b < bezierN; b++) {
22206 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22207 var manctrlptDist = void 0;
22208 var sign = signum(normctrlptDist);
22209
22210 if (multi) {
22211 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22212
22213 ctrlptWeight = ctrlptWs.value[b];
22214 }
22215
22216 if (edgeIsUnbundled) {
22217 // multi or single unbundled
22218 manctrlptDist = ctrlptDist;
22219 } else {
22220 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22221 }
22222
22223 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22224 var w1 = 1 - ctrlptWeight;
22225 var w2 = ctrlptWeight;
22226 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22227 var adjustedMidpt = {
22228 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22229 y: midptPts.y1 * w1 + midptPts.y2 * w2
22230 };
22231 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22232 }
22233};
22234
22235BRp$3.findTaxiPoints = function (edge, pairInfo) {
22236 // Taxicab geometry with two turns maximum
22237 var rs = edge._private.rscratch;
22238 rs.edgeType = 'segments';
22239 var VERTICAL = 'vertical';
22240 var HORIZONTAL = 'horizontal';
22241 var LEFTWARD = 'leftward';
22242 var RIGHTWARD = 'rightward';
22243 var DOWNWARD = 'downward';
22244 var UPWARD = 'upward';
22245 var AUTO = 'auto';
22246 var posPts = pairInfo.posPts,
22247 srcW = pairInfo.srcW,
22248 srcH = pairInfo.srcH,
22249 tgtW = pairInfo.tgtW,
22250 tgtH = pairInfo.tgtH;
22251 var edgeDistances = edge.pstyle('edge-distances').value;
22252 var dIncludesNodeBody = edgeDistances !== 'node-position';
22253 var taxiDir = edge.pstyle('taxi-direction').value;
22254 var rawTaxiDir = taxiDir; // unprocessed value
22255
22256 var taxiTurn = edge.pstyle('taxi-turn');
22257 var turnIsPercent = taxiTurn.units === '%';
22258 var taxiTurnPfVal = taxiTurn.pfValue;
22259 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
22260
22261 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22262 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22263 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22264 var pdx = posPts.x2 - posPts.x1;
22265 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
22266
22267 var subDWH = function subDWH(dxy, dwh) {
22268 if (dxy > 0) {
22269 return Math.max(dxy - dwh, 0);
22270 } else {
22271 return Math.min(dxy + dwh, 0);
22272 }
22273 };
22274
22275 var dx = subDWH(pdx, dw);
22276 var dy = subDWH(pdy, dh);
22277 var isExplicitDir = false;
22278
22279 if (rawTaxiDir === AUTO) {
22280 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22281 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
22282 taxiDir = VERTICAL;
22283 isExplicitDir = true;
22284 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
22285 taxiDir = HORIZONTAL;
22286 isExplicitDir = true;
22287 }
22288
22289 var isVert = taxiDir === VERTICAL;
22290 var l = isVert ? dy : dx;
22291 var pl = isVert ? pdy : pdx;
22292 var sgnL = signum(pl);
22293 var forcedDir = false;
22294
22295 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
22296 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22297 sgnL *= -1;
22298 l = sgnL * Math.abs(l);
22299 forcedDir = true;
22300 }
22301
22302 var d;
22303
22304 if (turnIsPercent) {
22305 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
22306 d = p * l;
22307 } else {
22308 var k = taxiTurnPfVal < 0 ? l : 0;
22309 d = k + taxiTurnPfVal * sgnL;
22310 }
22311
22312 var getIsTooClose = function getIsTooClose(d) {
22313 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22314 };
22315
22316 var isTooCloseSrc = getIsTooClose(d);
22317 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
22318 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22319
22320 if (isTooClose && !forcedDir) {
22321 // non-ideal routing
22322 if (isVert) {
22323 // vertical fallbacks
22324 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22325 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22326
22327 if (lShapeInsideSrc) {
22328 // horizontal Z-shape (direction not respected)
22329 var x = (posPts.x1 + posPts.x2) / 2;
22330 var y1 = posPts.y1,
22331 y2 = posPts.y2;
22332 rs.segpts = [x, y1, x, y2];
22333 } else if (lShapeInsideTgt) {
22334 // vertical Z-shape (distance not respected)
22335 var y = (posPts.y1 + posPts.y2) / 2;
22336 var x1 = posPts.x1,
22337 x2 = posPts.x2;
22338 rs.segpts = [x1, y, x2, y];
22339 } else {
22340 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22341 rs.segpts = [posPts.x1, posPts.y2];
22342 }
22343 } else {
22344 // horizontal fallbacks
22345 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22346
22347 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22348
22349 if (_lShapeInsideSrc) {
22350 // vertical Z-shape (direction not respected)
22351 var _y = (posPts.y1 + posPts.y2) / 2;
22352
22353 var _x = posPts.x1,
22354 _x2 = posPts.x2;
22355 rs.segpts = [_x, _y, _x2, _y];
22356 } else if (_lShapeInsideTgt) {
22357 // horizontal Z-shape (turn distance not respected)
22358 var _x3 = (posPts.x1 + posPts.x2) / 2;
22359
22360 var _y2 = posPts.y1,
22361 _y3 = posPts.y2;
22362 rs.segpts = [_x3, _y2, _x3, _y3];
22363 } else {
22364 // L-shape (turn distance not respected, but works well for tree siblings)
22365 rs.segpts = [posPts.x2, posPts.y1];
22366 }
22367 }
22368 } else {
22369 // ideal routing
22370 if (isVert) {
22371 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22372
22373 var _x4 = posPts.x1,
22374 _x5 = posPts.x2;
22375 rs.segpts = [_x4, _y4, _x5, _y4];
22376 } else {
22377 // horizontal
22378 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22379
22380 var _y5 = posPts.y1,
22381 _y6 = posPts.y2;
22382 rs.segpts = [_x6, _y5, _x6, _y6];
22383 }
22384 }
22385};
22386
22387BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22388 var rs = edge._private.rscratch; // can only correct beziers for now...
22389
22390 if (rs.edgeType === 'bezier') {
22391 var srcPos = pairInfo.srcPos,
22392 tgtPos = pairInfo.tgtPos,
22393 srcW = pairInfo.srcW,
22394 srcH = pairInfo.srcH,
22395 tgtW = pairInfo.tgtW,
22396 tgtH = pairInfo.tgtH,
22397 srcShape = pairInfo.srcShape,
22398 tgtShape = pairInfo.tgtShape;
22399 var badStart = !number(rs.startX) || !number(rs.startY);
22400 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
22401 var badEnd = !number(rs.endX) || !number(rs.endY);
22402 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
22403 var minCpADistFactor = 3;
22404 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22405 var minCpADist = minCpADistFactor * arrowW;
22406 var startACpDist = dist({
22407 x: rs.ctrlpts[0],
22408 y: rs.ctrlpts[1]
22409 }, {
22410 x: rs.startX,
22411 y: rs.startY
22412 });
22413 var closeStartACp = startACpDist < minCpADist;
22414 var endACpDist = dist({
22415 x: rs.ctrlpts[0],
22416 y: rs.ctrlpts[1]
22417 }, {
22418 x: rs.endX,
22419 y: rs.endY
22420 });
22421 var closeEndACp = endACpDist < minCpADist;
22422 var overlapping = false;
22423
22424 if (badStart || badAStart || closeStartACp) {
22425 overlapping = true; // project control point along line from src centre to outside the src shape
22426 // (otherwise intersection will yield nothing)
22427
22428 var cpD = {
22429 // delta
22430 x: rs.ctrlpts[0] - srcPos.x,
22431 y: rs.ctrlpts[1] - srcPos.y
22432 };
22433 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22434
22435 var cpM = {
22436 // normalised delta
22437 x: cpD.x / cpL,
22438 y: cpD.y / cpL
22439 };
22440 var radius = Math.max(srcW, srcH);
22441 var cpProj = {
22442 // *2 radius guarantees outside shape
22443 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22444 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22445 };
22446 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
22447
22448 if (closeStartACp) {
22449 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22450 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22451 } else {
22452 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22453 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22454 }
22455 }
22456
22457 if (badEnd || badAEnd || closeEndACp) {
22458 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
22459 // (otherwise intersection will yield nothing)
22460
22461 var _cpD = {
22462 // delta
22463 x: rs.ctrlpts[0] - tgtPos.x,
22464 y: rs.ctrlpts[1] - tgtPos.y
22465 };
22466
22467 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22468
22469
22470 var _cpM = {
22471 // normalised delta
22472 x: _cpD.x / _cpL,
22473 y: _cpD.y / _cpL
22474 };
22475
22476 var _radius = Math.max(srcW, srcH);
22477
22478 var _cpProj = {
22479 // *2 radius guarantees outside shape
22480 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22481 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22482 };
22483 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
22484
22485 if (closeEndACp) {
22486 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22487 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22488 } else {
22489 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22490 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22491 }
22492 }
22493
22494 if (overlapping) {
22495 // recalc endpts
22496 this.findEndpoints(edge);
22497 }
22498 }
22499};
22500
22501BRp$3.storeAllpts = function (edge) {
22502 var rs = edge._private.rscratch;
22503
22504 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22505 rs.allpts = [];
22506 rs.allpts.push(rs.startX, rs.startY);
22507
22508 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22509 // ctrl pt itself
22510 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
22511
22512 if (b + 3 < rs.ctrlpts.length) {
22513 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22514 }
22515 }
22516
22517 rs.allpts.push(rs.endX, rs.endY);
22518 var m, mt;
22519
22520 if (rs.ctrlpts.length / 2 % 2 === 0) {
22521 m = rs.allpts.length / 2 - 1;
22522 rs.midX = rs.allpts[m];
22523 rs.midY = rs.allpts[m + 1];
22524 } else {
22525 m = rs.allpts.length / 2 - 3;
22526 mt = 0.5;
22527 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22528 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22529 }
22530 } else if (rs.edgeType === 'straight') {
22531 // need to calc these after endpts
22532 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
22533
22534 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22535 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22536 } else if (rs.edgeType === 'segments') {
22537 rs.allpts = [];
22538 rs.allpts.push(rs.startX, rs.startY);
22539 rs.allpts.push.apply(rs.allpts, rs.segpts);
22540 rs.allpts.push(rs.endX, rs.endY);
22541
22542 if (rs.segpts.length % 4 === 0) {
22543 var i2 = rs.segpts.length / 2;
22544 var i1 = i2 - 2;
22545 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22546 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22547 } else {
22548 var _i = rs.segpts.length / 2 - 1;
22549
22550 rs.midX = rs.segpts[_i];
22551 rs.midY = rs.segpts[_i + 1];
22552 }
22553 }
22554};
22555
22556BRp$3.checkForInvalidEdgeWarning = function (edge) {
22557 var rs = edge[0]._private.rscratch;
22558
22559 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
22560 rs.loggedErr = false;
22561 } else {
22562 if (!rs.loggedErr) {
22563 rs.loggedErr = true;
22564 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.');
22565 }
22566 }
22567};
22568
22569BRp$3.findEdgeControlPoints = function (edges) {
22570 var _this = this;
22571
22572 if (!edges || edges.length === 0) {
22573 return;
22574 }
22575
22576 var r = this;
22577 var cy = r.cy;
22578 var hasCompounds = cy.hasCompoundNodes();
22579 var hashTable = {
22580 map: new Map$1(),
22581 get: function get(pairId) {
22582 var map2 = this.map.get(pairId[0]);
22583
22584 if (map2 != null) {
22585 return map2.get(pairId[1]);
22586 } else {
22587 return null;
22588 }
22589 },
22590 set: function set(pairId, val) {
22591 var map2 = this.map.get(pairId[0]);
22592
22593 if (map2 == null) {
22594 map2 = new Map$1();
22595 this.map.set(pairId[0], map2);
22596 }
22597
22598 map2.set(pairId[1], val);
22599 }
22600 };
22601 var pairIds = [];
22602 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
22603
22604 for (var i = 0; i < edges.length; i++) {
22605 var edge = edges[i];
22606 var _p = edge._private;
22607 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
22608 // they shouldn't take up space
22609
22610 if (edge.removed() || !edge.takesUpSpace()) {
22611 continue;
22612 }
22613
22614 if (curveStyle === 'haystack') {
22615 haystackEdges.push(edge);
22616 continue;
22617 }
22618
22619 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
22620 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
22621 var src = _p.source;
22622 var tgt = _p.target;
22623 var srcIndex = src.poolIndex();
22624 var tgtIndex = tgt.poolIndex();
22625 var pairId = [srcIndex, tgtIndex].sort();
22626 var tableEntry = hashTable.get(pairId);
22627
22628 if (tableEntry == null) {
22629 tableEntry = {
22630 eles: []
22631 };
22632 hashTable.set(pairId, tableEntry);
22633 pairIds.push(pairId);
22634 }
22635
22636 tableEntry.eles.push(edge);
22637
22638 if (edgeIsUnbundled) {
22639 tableEntry.hasUnbundled = true;
22640 }
22641
22642 if (edgeIsBezier) {
22643 tableEntry.hasBezier = true;
22644 }
22645 } // for each pair (src, tgt), create the ctrl pts
22646 // Nested for loop is OK; total number of iterations for both loops = edgeCount
22647
22648
22649 var _loop = function _loop(p) {
22650 var pairId = pairIds[p];
22651 var pairInfo = hashTable.get(pairId);
22652 var swappedpairInfo = void 0;
22653
22654 if (!pairInfo.hasUnbundled) {
22655 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
22656 return e.isBundledBezier();
22657 });
22658 clearArray(pairInfo.eles);
22659 pllEdges.forEach(function (edge) {
22660 return pairInfo.eles.push(edge);
22661 }); // for each pair id, the edges should be sorted by index
22662
22663 pairInfo.eles.sort(function (edge1, edge2) {
22664 return edge1.poolIndex() - edge2.poolIndex();
22665 });
22666 }
22667
22668 var firstEdge = pairInfo.eles[0];
22669 var src = firstEdge.source();
22670 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
22671
22672 if (src.poolIndex() > tgt.poolIndex()) {
22673 var temp = src;
22674 src = tgt;
22675 tgt = temp;
22676 }
22677
22678 var srcPos = pairInfo.srcPos = src.position();
22679 var tgtPos = pairInfo.tgtPos = tgt.position();
22680 var srcW = pairInfo.srcW = src.outerWidth();
22681 var srcH = pairInfo.srcH = src.outerHeight();
22682 var tgtW = pairInfo.tgtW = tgt.outerWidth();
22683 var tgtH = pairInfo.tgtH = tgt.outerHeight();
22684
22685 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
22686
22687 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
22688
22689 pairInfo.dirCounts = {
22690 'north': 0,
22691 'west': 0,
22692 'south': 0,
22693 'east': 0,
22694 'northwest': 0,
22695 'southwest': 0,
22696 'northeast': 0,
22697 'southeast': 0
22698 };
22699
22700 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
22701 var _edge = pairInfo.eles[_i2];
22702 var rs = _edge[0]._private.rscratch;
22703
22704 var _curveStyle = _edge.pstyle('curve-style').value;
22705
22706 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
22707
22708
22709 var edgeIsSwapped = !src.same(_edge.source());
22710
22711 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
22712 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
22713
22714 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
22715 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
22716
22717 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
22718 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
22719 var intersectionPts = pairInfo.intersectionPts = {
22720 x1: srcOutside[0],
22721 x2: tgtOutside[0],
22722 y1: srcOutside[1],
22723 y2: tgtOutside[1]
22724 };
22725 var posPts = pairInfo.posPts = {
22726 x1: srcPos.x,
22727 x2: tgtPos.x,
22728 y1: srcPos.y,
22729 y2: tgtPos.y
22730 };
22731 var dy = tgtOutside[1] - srcOutside[1];
22732 var dx = tgtOutside[0] - srcOutside[0];
22733 var l = Math.sqrt(dx * dx + dy * dy);
22734 var vector = pairInfo.vector = {
22735 x: dx,
22736 y: dy
22737 };
22738 var vectorNorm = pairInfo.vectorNorm = {
22739 x: vector.x / l,
22740 y: vector.y / l
22741 };
22742 var vectorNormInverse = {
22743 x: -vectorNorm.y,
22744 y: vectorNorm.x
22745 }; // if node shapes overlap, then no ctrl pts to draw
22746
22747 pairInfo.nodesOverlap = !number(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);
22748 pairInfo.vectorNormInverse = vectorNormInverse;
22749 swappedpairInfo = {
22750 nodesOverlap: pairInfo.nodesOverlap,
22751 dirCounts: pairInfo.dirCounts,
22752 calculatedIntersection: true,
22753 hasBezier: pairInfo.hasBezier,
22754 hasUnbundled: pairInfo.hasUnbundled,
22755 eles: pairInfo.eles,
22756 srcPos: tgtPos,
22757 tgtPos: srcPos,
22758 srcW: tgtW,
22759 srcH: tgtH,
22760 tgtW: srcW,
22761 tgtH: srcH,
22762 srcIntn: tgtIntn,
22763 tgtIntn: srcIntn,
22764 srcShape: tgtShape,
22765 tgtShape: srcShape,
22766 posPts: {
22767 x1: posPts.x2,
22768 y1: posPts.y2,
22769 x2: posPts.x1,
22770 y2: posPts.y1
22771 },
22772 intersectionPts: {
22773 x1: intersectionPts.x2,
22774 y1: intersectionPts.y2,
22775 x2: intersectionPts.x1,
22776 y2: intersectionPts.y1
22777 },
22778 vector: {
22779 x: -vector.x,
22780 y: -vector.y
22781 },
22782 vectorNorm: {
22783 x: -vectorNorm.x,
22784 y: -vectorNorm.y
22785 },
22786 vectorNormInverse: {
22787 x: -vectorNormInverse.x,
22788 y: -vectorNormInverse.y
22789 }
22790 };
22791 }
22792
22793 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
22794 rs.nodesOverlap = passedPairInfo.nodesOverlap;
22795 rs.srcIntn = passedPairInfo.srcIntn;
22796 rs.tgtIntn = passedPairInfo.tgtIntn;
22797
22798 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
22799 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22800 } else if (src === tgt) {
22801 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22802 } else if (_curveStyle === 'segments') {
22803 _this.findSegmentsPoints(_edge, passedPairInfo);
22804 } else if (_curveStyle === 'taxi') {
22805 _this.findTaxiPoints(_edge, passedPairInfo);
22806 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
22807 _this.findStraightEdgePoints(_edge);
22808 } else {
22809 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
22810 }
22811
22812 _this.findEndpoints(_edge);
22813
22814 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
22815
22816 _this.checkForInvalidEdgeWarning(_edge);
22817
22818 _this.storeAllpts(_edge);
22819
22820 _this.storeEdgeProjections(_edge);
22821
22822 _this.calculateArrowAngles(_edge);
22823
22824 _this.recalculateEdgeLabelProjections(_edge);
22825
22826 _this.calculateLabelAngles(_edge);
22827 } // for pair edges
22828
22829 };
22830
22831 for (var p = 0; p < pairIds.length; p++) {
22832 _loop(p);
22833 } // for pair ids
22834 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
22835
22836
22837 this.findHaystackPoints(haystackEdges);
22838};
22839
22840function getPts(pts) {
22841 var retPts = [];
22842
22843 if (pts == null) {
22844 return;
22845 }
22846
22847 for (var i = 0; i < pts.length; i += 2) {
22848 var x = pts[i];
22849 var y = pts[i + 1];
22850 retPts.push({
22851 x: x,
22852 y: y
22853 });
22854 }
22855
22856 return retPts;
22857}
22858
22859BRp$3.getSegmentPoints = function (edge) {
22860 var rs = edge[0]._private.rscratch;
22861 var type = rs.edgeType;
22862
22863 if (type === 'segments') {
22864 this.recalculateRenderedStyle(edge);
22865 return getPts(rs.segpts);
22866 }
22867};
22868
22869BRp$3.getControlPoints = function (edge) {
22870 var rs = edge[0]._private.rscratch;
22871 var type = rs.edgeType;
22872
22873 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
22874 this.recalculateRenderedStyle(edge);
22875 return getPts(rs.ctrlpts);
22876 }
22877};
22878
22879BRp$3.getEdgeMidpoint = function (edge) {
22880 var rs = edge[0]._private.rscratch;
22881 this.recalculateRenderedStyle(edge);
22882 return {
22883 x: rs.midX,
22884 y: rs.midY
22885 };
22886};
22887
22888var BRp$4 = {};
22889
22890BRp$4.manualEndptToPx = function (node, prop) {
22891 var r = this;
22892 var npos = node.position();
22893 var w = node.outerWidth();
22894 var h = node.outerHeight();
22895
22896 if (prop.value.length === 2) {
22897 var p = [prop.pfValue[0], prop.pfValue[1]];
22898
22899 if (prop.units[0] === '%') {
22900 p[0] = p[0] * w;
22901 }
22902
22903 if (prop.units[1] === '%') {
22904 p[1] = p[1] * h;
22905 }
22906
22907 p[0] += npos.x;
22908 p[1] += npos.y;
22909 return p;
22910 } else {
22911 var angle = prop.pfValue[0];
22912 angle = -Math.PI / 2 + angle; // start at 12 o'clock
22913
22914 var l = 2 * Math.max(w, h);
22915 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
22916 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
22917 }
22918};
22919
22920BRp$4.findEndpoints = function (edge) {
22921 var r = this;
22922 var intersect;
22923 var source = edge.source()[0];
22924 var target = edge.target()[0];
22925 var srcPos = source.position();
22926 var tgtPos = target.position();
22927 var tgtArShape = edge.pstyle('target-arrow-shape').value;
22928 var srcArShape = edge.pstyle('source-arrow-shape').value;
22929 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
22930 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
22931 var curveStyle = edge.pstyle('curve-style').value;
22932 var rs = edge._private.rscratch;
22933 var et = rs.edgeType;
22934 var taxi = curveStyle === 'taxi';
22935 var self = et === 'self' || et === 'compound';
22936 var bezier = et === 'bezier' || et === 'multibezier' || self;
22937 var multi = et !== 'bezier';
22938 var lines = et === 'straight' || et === 'segments';
22939 var segments = et === 'segments';
22940 var hasEndpts = bezier || multi || lines;
22941 var overrideEndpts = self || taxi;
22942 var srcManEndpt = edge.pstyle('source-endpoint');
22943 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
22944 var tgtManEndpt = edge.pstyle('target-endpoint');
22945 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
22946 rs.srcManEndpt = srcManEndpt;
22947 rs.tgtManEndpt = tgtManEndpt;
22948 var p1; // last known point of edge on target side
22949
22950 var p2; // last known point of edge on source side
22951
22952 var p1_i; // point to intersect with target shape
22953
22954 var p2_i; // point to intersect with source shape
22955
22956 if (bezier) {
22957 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
22958 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
22959 p1 = cpEnd;
22960 p2 = cpStart;
22961 } else if (lines) {
22962 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
22963 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
22964 p1 = tgtArrowFromPt;
22965 p2 = srcArrowFromPt;
22966 }
22967
22968 if (tgtManEndptVal === 'inside-to-node') {
22969 intersect = [tgtPos.x, tgtPos.y];
22970 } else if (tgtManEndpt.units) {
22971 intersect = this.manualEndptToPx(target, tgtManEndpt);
22972 } else if (tgtManEndptVal === 'outside-to-line') {
22973 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
22974 } else {
22975 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
22976 p1_i = p1;
22977 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
22978 p1_i = [srcPos.x, srcPos.y];
22979 }
22980
22981 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
22982
22983 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
22984 var trs = target._private.rscratch;
22985 var lw = trs.labelWidth;
22986 var lh = trs.labelHeight;
22987 var lx = trs.labelX;
22988 var ly = trs.labelY;
22989 var lw2 = lw / 2;
22990 var lh2 = lh / 2;
22991 var va = target.pstyle('text-valign').value;
22992
22993 if (va === 'top') {
22994 ly -= lh2;
22995 } else if (va === 'bottom') {
22996 ly += lh2;
22997 }
22998
22999 var ha = target.pstyle('text-halign').value;
23000
23001 if (ha === 'left') {
23002 lx -= lw2;
23003 } else if (ha === 'right') {
23004 lx += lw2;
23005 }
23006
23007 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);
23008
23009 if (labelIntersect.length > 0) {
23010 var refPt = srcPos;
23011 var intSqdist = sqdist(refPt, array2point(intersect));
23012 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23013 var minSqDist = intSqdist;
23014
23015 if (labIntSqdist < intSqdist) {
23016 intersect = labelIntersect;
23017 minSqDist = labIntSqdist;
23018 }
23019
23020 if (labelIntersect.length > 2) {
23021 var labInt2SqDist = sqdist(refPt, {
23022 x: labelIntersect[2],
23023 y: labelIntersect[3]
23024 });
23025
23026 if (labInt2SqDist < minSqDist) {
23027 intersect = [labelIntersect[2], labelIntersect[3]];
23028 }
23029 }
23030 }
23031 }
23032 }
23033
23034 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23035 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23036 rs.endX = edgeEnd[0];
23037 rs.endY = edgeEnd[1];
23038 rs.arrowEndX = arrowEnd[0];
23039 rs.arrowEndY = arrowEnd[1];
23040
23041 if (srcManEndptVal === 'inside-to-node') {
23042 intersect = [srcPos.x, srcPos.y];
23043 } else if (srcManEndpt.units) {
23044 intersect = this.manualEndptToPx(source, srcManEndpt);
23045 } else if (srcManEndptVal === 'outside-to-line') {
23046 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23047 } else {
23048 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23049 p2_i = p2;
23050 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23051 p2_i = [tgtPos.x, tgtPos.y];
23052 }
23053
23054 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23055
23056 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23057 var srs = source._private.rscratch;
23058 var _lw = srs.labelWidth;
23059 var _lh = srs.labelHeight;
23060 var _lx = srs.labelX;
23061 var _ly = srs.labelY;
23062
23063 var _lw2 = _lw / 2;
23064
23065 var _lh2 = _lh / 2;
23066
23067 var _va = source.pstyle('text-valign').value;
23068
23069 if (_va === 'top') {
23070 _ly -= _lh2;
23071 } else if (_va === 'bottom') {
23072 _ly += _lh2;
23073 }
23074
23075 var _ha = source.pstyle('text-halign').value;
23076
23077 if (_ha === 'left') {
23078 _lx -= _lw2;
23079 } else if (_ha === 'right') {
23080 _lx += _lw2;
23081 }
23082
23083 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);
23084
23085 if (_labelIntersect.length > 0) {
23086 var _refPt = tgtPos;
23087
23088 var _intSqdist = sqdist(_refPt, array2point(intersect));
23089
23090 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23091
23092 var _minSqDist = _intSqdist;
23093
23094 if (_labIntSqdist < _intSqdist) {
23095 intersect = [_labelIntersect[0], _labelIntersect[1]];
23096 _minSqDist = _labIntSqdist;
23097 }
23098
23099 if (_labelIntersect.length > 2) {
23100 var _labInt2SqDist = sqdist(_refPt, {
23101 x: _labelIntersect[2],
23102 y: _labelIntersect[3]
23103 });
23104
23105 if (_labInt2SqDist < _minSqDist) {
23106 intersect = [_labelIntersect[2], _labelIntersect[3]];
23107 }
23108 }
23109 }
23110 }
23111 }
23112
23113 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23114 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23115 rs.startX = edgeStart[0];
23116 rs.startY = edgeStart[1];
23117 rs.arrowStartX = arrowStart[0];
23118 rs.arrowStartY = arrowStart[1];
23119
23120 if (hasEndpts) {
23121 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
23122 rs.badLine = true;
23123 } else {
23124 rs.badLine = false;
23125 }
23126 }
23127};
23128
23129BRp$4.getSourceEndpoint = function (edge) {
23130 var rs = edge[0]._private.rscratch;
23131 this.recalculateRenderedStyle(edge);
23132
23133 switch (rs.edgeType) {
23134 case 'haystack':
23135 return {
23136 x: rs.haystackPts[0],
23137 y: rs.haystackPts[1]
23138 };
23139
23140 default:
23141 return {
23142 x: rs.arrowStartX,
23143 y: rs.arrowStartY
23144 };
23145 }
23146};
23147
23148BRp$4.getTargetEndpoint = function (edge) {
23149 var rs = edge[0]._private.rscratch;
23150 this.recalculateRenderedStyle(edge);
23151
23152 switch (rs.edgeType) {
23153 case 'haystack':
23154 return {
23155 x: rs.haystackPts[2],
23156 y: rs.haystackPts[3]
23157 };
23158
23159 default:
23160 return {
23161 x: rs.arrowEndX,
23162 y: rs.arrowEndY
23163 };
23164 }
23165};
23166
23167var BRp$5 = {};
23168
23169function pushBezierPts(r, edge, pts) {
23170 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23171 return qbezierAt(p1, p2, p3, t);
23172 };
23173
23174 var _p = edge._private;
23175 var bpts = _p.rstyle.bezierPts;
23176
23177 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23178 var p = r.bezierProjPcts[i];
23179 bpts.push({
23180 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23181 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23182 });
23183 }
23184}
23185
23186BRp$5.storeEdgeProjections = function (edge) {
23187 var _p = edge._private;
23188 var rs = _p.rscratch;
23189 var et = rs.edgeType; // clear the cached points state
23190
23191 _p.rstyle.bezierPts = null;
23192 _p.rstyle.linePts = null;
23193 _p.rstyle.haystackPts = null;
23194
23195 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23196 _p.rstyle.bezierPts = [];
23197
23198 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23199 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23200 }
23201 } else if (et === 'segments') {
23202 var lpts = _p.rstyle.linePts = [];
23203
23204 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23205 lpts.push({
23206 x: rs.allpts[i],
23207 y: rs.allpts[i + 1]
23208 });
23209 }
23210 } else if (et === 'haystack') {
23211 var hpts = rs.haystackPts;
23212 _p.rstyle.haystackPts = [{
23213 x: hpts[0],
23214 y: hpts[1]
23215 }, {
23216 x: hpts[2],
23217 y: hpts[3]
23218 }];
23219 }
23220
23221 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23222};
23223
23224BRp$5.recalculateEdgeProjections = function (edges) {
23225 this.findEdgeControlPoints(edges);
23226};
23227
23228/* global document */
23229
23230var BRp$6 = {};
23231
23232BRp$6.recalculateNodeLabelProjection = function (node) {
23233 var content = node.pstyle('label').strValue;
23234
23235 if (emptyString(content)) {
23236 return;
23237 }
23238
23239 var textX, textY;
23240 var _p = node._private;
23241 var nodeWidth = node.width();
23242 var nodeHeight = node.height();
23243 var padding = node.padding();
23244 var nodePos = node.position();
23245 var textHalign = node.pstyle('text-halign').strValue;
23246 var textValign = node.pstyle('text-valign').strValue;
23247 var rs = _p.rscratch;
23248 var rstyle = _p.rstyle;
23249
23250 switch (textHalign) {
23251 case 'left':
23252 textX = nodePos.x - nodeWidth / 2 - padding;
23253 break;
23254
23255 case 'right':
23256 textX = nodePos.x + nodeWidth / 2 + padding;
23257 break;
23258
23259 default:
23260 // e.g. center
23261 textX = nodePos.x;
23262 }
23263
23264 switch (textValign) {
23265 case 'top':
23266 textY = nodePos.y - nodeHeight / 2 - padding;
23267 break;
23268
23269 case 'bottom':
23270 textY = nodePos.y + nodeHeight / 2 + padding;
23271 break;
23272
23273 default:
23274 // e.g. middle
23275 textY = nodePos.y;
23276 }
23277
23278 rs.labelX = textX;
23279 rs.labelY = textY;
23280 rstyle.labelX = textX;
23281 rstyle.labelY = textY;
23282 this.calculateLabelAngles(node);
23283 this.applyLabelDimensions(node);
23284};
23285
23286var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23287 var angle = Math.atan(dy / dx);
23288
23289 if (dx === 0 && angle < 0) {
23290 angle = angle * -1;
23291 }
23292
23293 return angle;
23294};
23295
23296var lineAngle = function lineAngle(p0, p1) {
23297 var dx = p1.x - p0.x;
23298 var dy = p1.y - p0.y;
23299 return lineAngleFromDelta(dx, dy);
23300};
23301
23302var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23303 var t0 = bound(0, t - 0.001, 1);
23304 var t1 = bound(0, t + 0.001, 1);
23305 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23306 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23307 return lineAngle(lp0, lp1);
23308};
23309
23310BRp$6.recalculateEdgeLabelProjections = function (edge) {
23311 var p;
23312 var _p = edge._private;
23313 var rs = _p.rscratch;
23314 var r = this;
23315 var content = {
23316 mid: edge.pstyle('label').strValue,
23317 source: edge.pstyle('source-label').strValue,
23318 target: edge.pstyle('target-label').strValue
23319 };
23320
23321 if (content.mid || content.source || content.target) ; else {
23322 return; // no labels => no calcs
23323 } // add center point to style so bounding box calculations can use it
23324 //
23325
23326
23327 p = {
23328 x: rs.midX,
23329 y: rs.midY
23330 };
23331
23332 var setRs = function setRs(propName, prefix, value) {
23333 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23334 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23335 };
23336
23337 setRs('labelX', null, p.x);
23338 setRs('labelY', null, p.y);
23339 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23340 setRs('labelAutoAngle', null, midAngle);
23341
23342 var createControlPointInfo = function createControlPointInfo() {
23343 if (createControlPointInfo.cache) {
23344 return createControlPointInfo.cache;
23345 } // use cache so only 1x per edge
23346
23347
23348 var ctrlpts = []; // store each ctrlpt info init
23349
23350 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23351 var p0 = {
23352 x: rs.allpts[i],
23353 y: rs.allpts[i + 1]
23354 };
23355 var p1 = {
23356 x: rs.allpts[i + 2],
23357 y: rs.allpts[i + 3]
23358 }; // ctrlpt
23359
23360 var p2 = {
23361 x: rs.allpts[i + 4],
23362 y: rs.allpts[i + 5]
23363 };
23364 ctrlpts.push({
23365 p0: p0,
23366 p1: p1,
23367 p2: p2,
23368 startDist: 0,
23369 length: 0,
23370 segments: []
23371 });
23372 }
23373
23374 var bpts = _p.rstyle.bezierPts;
23375 var nProjs = r.bezierProjPcts.length;
23376
23377 function addSegment(cp, p0, p1, t0, t1) {
23378 var length = dist(p0, p1);
23379 var prevSegment = cp.segments[cp.segments.length - 1];
23380 var segment = {
23381 p0: p0,
23382 p1: p1,
23383 t0: t0,
23384 t1: t1,
23385 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23386 length: length
23387 };
23388 cp.segments.push(segment);
23389 cp.length += length;
23390 } // update each ctrlpt with segment info
23391
23392
23393 for (var _i = 0; _i < ctrlpts.length; _i++) {
23394 var cp = ctrlpts[_i];
23395 var prevCp = ctrlpts[_i - 1];
23396
23397 if (prevCp) {
23398 cp.startDist = prevCp.startDist + prevCp.length;
23399 }
23400
23401 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23402
23403 for (var j = 0; j < nProjs - 1; j++) {
23404 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23405 }
23406
23407 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23408 }
23409
23410 return createControlPointInfo.cache = ctrlpts;
23411 };
23412
23413 var calculateEndProjection = function calculateEndProjection(prefix) {
23414 var angle;
23415 var isSrc = prefix === 'source';
23416
23417 if (!content[prefix]) {
23418 return;
23419 }
23420
23421 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23422
23423 switch (rs.edgeType) {
23424 case 'self':
23425 case 'compound':
23426 case 'bezier':
23427 case 'multibezier':
23428 {
23429 var cps = createControlPointInfo();
23430 var selected;
23431 var startDist = 0;
23432 var totalDist = 0; // find the segment we're on
23433
23434 for (var i = 0; i < cps.length; i++) {
23435 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23436
23437 for (var j = 0; j < _cp.segments.length; j++) {
23438 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23439 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23440 startDist = totalDist;
23441 totalDist += _seg.length;
23442
23443 if (totalDist >= offset || lastSeg) {
23444 selected = {
23445 cp: _cp,
23446 segment: _seg
23447 };
23448 break;
23449 }
23450 }
23451
23452 if (selected) {
23453 break;
23454 }
23455 }
23456
23457 var cp = selected.cp;
23458 var seg = selected.segment;
23459 var tSegment = (offset - startDist) / seg.length;
23460 var segDt = seg.t1 - seg.t0;
23461 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23462 t = bound(0, t, 1);
23463 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23464 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23465 break;
23466 }
23467
23468 case 'straight':
23469 case 'segments':
23470 case 'haystack':
23471 {
23472 var d = 0,
23473 di,
23474 d0;
23475 var p0, p1;
23476 var l = rs.allpts.length;
23477
23478 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23479 if (isSrc) {
23480 p0 = {
23481 x: rs.allpts[_i2],
23482 y: rs.allpts[_i2 + 1]
23483 };
23484 p1 = {
23485 x: rs.allpts[_i2 + 2],
23486 y: rs.allpts[_i2 + 3]
23487 };
23488 } else {
23489 p0 = {
23490 x: rs.allpts[l - 2 - _i2],
23491 y: rs.allpts[l - 1 - _i2]
23492 };
23493 p1 = {
23494 x: rs.allpts[l - 4 - _i2],
23495 y: rs.allpts[l - 3 - _i2]
23496 };
23497 }
23498
23499 di = dist(p0, p1);
23500 d0 = d;
23501 d += di;
23502
23503 if (d >= offset) {
23504 break;
23505 }
23506 }
23507
23508 var pD = offset - d0;
23509
23510 var _t = pD / di;
23511
23512 _t = bound(0, _t, 1);
23513 p = lineAt(p0, p1, _t);
23514 angle = lineAngle(p0, p1);
23515 break;
23516 }
23517 }
23518
23519 setRs('labelX', prefix, p.x);
23520 setRs('labelY', prefix, p.y);
23521 setRs('labelAutoAngle', prefix, angle);
23522 };
23523
23524 calculateEndProjection('source');
23525 calculateEndProjection('target');
23526 this.applyLabelDimensions(edge);
23527};
23528
23529BRp$6.applyLabelDimensions = function (ele) {
23530 this.applyPrefixedLabelDimensions(ele);
23531
23532 if (ele.isEdge()) {
23533 this.applyPrefixedLabelDimensions(ele, 'source');
23534 this.applyPrefixedLabelDimensions(ele, 'target');
23535 }
23536};
23537
23538BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
23539 var _p = ele._private;
23540 var text = this.getLabelText(ele, prefix);
23541 var labelDims = this.calculateLabelDimensions(ele, text);
23542 var lineHeight = ele.pstyle('line-height').pfValue;
23543 var textWrap = ele.pstyle('text-wrap').strValue;
23544 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23545 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23546 var normPerLineHeight = labelDims.height / numLines;
23547 var labelLineHeight = normPerLineHeight * lineHeight;
23548 var width = labelDims.width;
23549 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23550 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23551 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23552 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23553 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23554 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23555};
23556
23557BRp$6.getLabelText = function (ele, prefix) {
23558 var _p = ele._private;
23559 var pfd = prefix ? prefix + '-' : '';
23560 var text = ele.pstyle(pfd + 'label').strValue;
23561 var textTransform = ele.pstyle('text-transform').value;
23562
23563 var rscratch = function rscratch(propName, value) {
23564 if (value) {
23565 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23566 return value;
23567 } else {
23568 return getPrefixedProperty(_p.rscratch, propName, prefix);
23569 }
23570 }; // for empty text, skip all processing
23571
23572
23573 if (!text) {
23574 return '';
23575 }
23576
23577 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23578 text = text.toUpperCase();
23579 } else if (textTransform == 'lowercase') {
23580 text = text.toLowerCase();
23581 }
23582
23583 var wrapStyle = ele.pstyle('text-wrap').value;
23584
23585 if (wrapStyle === 'wrap') {
23586 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
23587
23588 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23589 return rscratch('labelWrapCachedText');
23590 }
23591
23592 var zwsp = "\u200B";
23593 var lines = text.split('\n');
23594 var maxW = ele.pstyle('text-max-width').pfValue;
23595 var overflow = ele.pstyle('text-overflow-wrap').value;
23596 var overflowAny = overflow === 'anywhere';
23597 var wrappedLines = [];
23598 var wordsRegex = /[\s\u200b]+/;
23599 var wordSeparator = overflowAny ? '' : ' ';
23600
23601 for (var l = 0; l < lines.length; l++) {
23602 var line = lines[l];
23603 var lineDims = this.calculateLabelDimensions(ele, line);
23604 var lineW = lineDims.width;
23605
23606 if (overflowAny) {
23607 var processedLine = line.split('').join(zwsp);
23608 line = processedLine;
23609 }
23610
23611 if (lineW > maxW) {
23612 // line is too long
23613 var words = line.split(wordsRegex);
23614 var subline = '';
23615
23616 for (var w = 0; w < words.length; w++) {
23617 var word = words[w];
23618 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
23619 var testDims = this.calculateLabelDimensions(ele, testLine);
23620 var testW = testDims.width;
23621
23622 if (testW <= maxW) {
23623 // word fits on current line
23624 subline += word + wordSeparator;
23625 } else {
23626 // word starts new line
23627 if (subline) {
23628 wrappedLines.push(subline);
23629 }
23630
23631 subline = word + wordSeparator;
23632 }
23633 } // if there's remaining text, put it in a wrapped line
23634
23635
23636 if (!subline.match(/^[\s\u200b]+$/)) {
23637 wrappedLines.push(subline);
23638 }
23639 } else {
23640 // line is already short enough
23641 wrappedLines.push(line);
23642 }
23643 } // for
23644
23645
23646 rscratch('labelWrapCachedLines', wrappedLines);
23647 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23648 rscratch('labelWrapKey', labelKey);
23649 } else if (wrapStyle === 'ellipsis') {
23650 var _maxW = ele.pstyle('text-max-width').pfValue;
23651 var ellipsized = '';
23652 var ellipsis = "\u2026";
23653 var incLastCh = false;
23654
23655 if (this.calculateLabelDimensions(ele, text).width < _maxW) {
23656 // the label already fits
23657 return text;
23658 }
23659
23660 for (var i = 0; i < text.length; i++) {
23661 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23662
23663 if (widthWithNextCh > _maxW) {
23664 break;
23665 }
23666
23667 ellipsized += text[i];
23668
23669 if (i === text.length - 1) {
23670 incLastCh = true;
23671 }
23672 }
23673
23674 if (!incLastCh) {
23675 ellipsized += ellipsis;
23676 }
23677
23678 return ellipsized;
23679 } // if ellipsize
23680
23681
23682 return text;
23683};
23684
23685BRp$6.getLabelJustification = function (ele) {
23686 var justification = ele.pstyle('text-justification').strValue;
23687 var textHalign = ele.pstyle('text-halign').strValue;
23688
23689 if (justification === 'auto') {
23690 if (ele.isNode()) {
23691 switch (textHalign) {
23692 case 'left':
23693 return 'right';
23694
23695 case 'right':
23696 return 'left';
23697
23698 default:
23699 return 'center';
23700 }
23701 } else {
23702 return 'center';
23703 }
23704 } else {
23705 return justification;
23706 }
23707};
23708
23709BRp$6.calculateLabelDimensions = function (ele, text) {
23710 var r = this;
23711 var cacheKey = hashString(text, ele._private.labelDimsKey);
23712 var cache = r.labelDimCache || (r.labelDimCache = []);
23713 var existingVal = cache[cacheKey];
23714
23715 if (existingVal != null) {
23716 return existingVal;
23717 }
23718
23719 var padding = 0; // add padding around text dims, as the measurement isn't that accurate
23720
23721 var fStyle = ele.pstyle('font-style').strValue;
23722 var size = ele.pstyle('font-size').pfValue;
23723 var family = ele.pstyle('font-family').strValue;
23724 var weight = ele.pstyle('font-weight').strValue;
23725 var canvas = this.labelCalcCanvas;
23726 var c2d = this.labelCalcCanvasContext;
23727
23728 if (!canvas) {
23729 canvas = this.labelCalcCanvas = document.createElement('canvas');
23730 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
23731 var ds = canvas.style;
23732 ds.position = 'absolute';
23733 ds.left = '-9999px';
23734 ds.top = '-9999px';
23735 ds.zIndex = '-1';
23736 ds.visibility = 'hidden';
23737 ds.pointerEvents = 'none';
23738 }
23739
23740 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
23741 var width = 0;
23742 var height = 0;
23743 var lines = text.split('\n');
23744
23745 for (var i = 0; i < lines.length; i++) {
23746 var line = lines[i];
23747 var metrics = c2d.measureText(line);
23748 var w = Math.ceil(metrics.width);
23749 var h = size;
23750 width = Math.max(w, width);
23751 height += h;
23752 }
23753
23754 width += padding;
23755 height += padding;
23756 return cache[cacheKey] = {
23757 width: width,
23758 height: height
23759 };
23760};
23761
23762BRp$6.calculateLabelAngle = function (ele, prefix) {
23763 var _p = ele._private;
23764 var rs = _p.rscratch;
23765 var isEdge = ele.isEdge();
23766 var prefixDash = prefix ? prefix + '-' : '';
23767 var rot = ele.pstyle(prefixDash + 'text-rotation');
23768 var rotStr = rot.strValue;
23769
23770 if (rotStr === 'none') {
23771 return 0;
23772 } else if (isEdge && rotStr === 'autorotate') {
23773 return rs.labelAutoAngle;
23774 } else if (rotStr === 'autorotate') {
23775 return 0;
23776 } else {
23777 return rot.pfValue;
23778 }
23779};
23780
23781BRp$6.calculateLabelAngles = function (ele) {
23782 var r = this;
23783 var isEdge = ele.isEdge();
23784 var _p = ele._private;
23785 var rs = _p.rscratch;
23786 rs.labelAngle = r.calculateLabelAngle(ele);
23787
23788 if (isEdge) {
23789 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
23790 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
23791 }
23792};
23793
23794var BRp$7 = {};
23795var TOO_SMALL_CUT_RECT = 28;
23796var warnedCutRect = false;
23797
23798BRp$7.getNodeShape = function (node) {
23799 var r = this;
23800 var shape = node.pstyle('shape').value;
23801
23802 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
23803 if (!warnedCutRect) {
23804 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
23805 warnedCutRect = true;
23806 }
23807
23808 return 'rectangle';
23809 }
23810
23811 if (node.isParent()) {
23812 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
23813 return shape;
23814 } else {
23815 return 'rectangle';
23816 }
23817 }
23818
23819 if (shape === 'polygon') {
23820 var points = node.pstyle('shape-polygon-points').value;
23821 return r.nodeShapes.makePolygon(points).name;
23822 }
23823
23824 return shape;
23825};
23826
23827var BRp$8 = {};
23828
23829BRp$8.registerCalculationListeners = function () {
23830 var cy = this.cy;
23831 var elesToUpdate = cy.collection();
23832 var r = this;
23833
23834 var enqueue = function enqueue(eles) {
23835 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
23836 elesToUpdate.merge(eles);
23837
23838 if (dirtyStyleCaches) {
23839 for (var i = 0; i < eles.length; i++) {
23840 var ele = eles[i];
23841 var _p = ele._private;
23842 var rstyle = _p.rstyle;
23843 rstyle.clean = false;
23844 rstyle.cleanConnected = false;
23845 }
23846 }
23847 };
23848
23849 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
23850 var ele = e.target;
23851 enqueue(ele);
23852 }).on('style.* background.*', function onDirtyStyle(e) {
23853 var ele = e.target;
23854 enqueue(ele, false);
23855 });
23856
23857 var updateEleCalcs = function updateEleCalcs(willDraw) {
23858 if (willDraw) {
23859 var fns = r.onUpdateEleCalcsFns; // because we need to have up-to-date style (e.g. stylesheet mappers)
23860 // before calculating rendered style (and pstyle might not be called yet)
23861
23862 elesToUpdate.cleanStyle();
23863
23864 for (var i = 0; i < elesToUpdate.length; i++) {
23865 var ele = elesToUpdate[i];
23866 var rstyle = ele._private.rstyle;
23867
23868 if (ele.isNode() && !rstyle.cleanConnected) {
23869 enqueue(ele.connectedEdges());
23870 rstyle.cleanConnected = true;
23871 }
23872 }
23873
23874 if (fns) {
23875 for (var _i = 0; _i < fns.length; _i++) {
23876 var fn = fns[_i];
23877 fn(willDraw, elesToUpdate);
23878 }
23879 }
23880
23881 r.recalculateRenderedStyle(elesToUpdate);
23882 elesToUpdate = cy.collection();
23883 }
23884 };
23885
23886 r.flushRenderedStyleQueue = function () {
23887 updateEleCalcs(true);
23888 };
23889
23890 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
23891};
23892
23893BRp$8.onUpdateEleCalcs = function (fn) {
23894 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
23895 fns.push(fn);
23896};
23897
23898BRp$8.recalculateRenderedStyle = function (eles, useCache) {
23899 var isCleanConnected = function isCleanConnected(ele) {
23900 return ele._private.rstyle.cleanConnected;
23901 };
23902
23903 var edges = [];
23904 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
23905
23906 if (this.destroyed) {
23907 return;
23908 } // use cache by default for perf
23909
23910
23911 if (useCache === undefined) {
23912 useCache = true;
23913 }
23914
23915 for (var i = 0; i < eles.length; i++) {
23916 var ele = eles[i];
23917 var _p = ele._private;
23918 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
23919 // (and a request for recalc may come in between frames)
23920
23921 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
23922 rstyle.clean = false;
23923 } // only update if dirty and in graph
23924
23925
23926 if (useCache && rstyle.clean || ele.removed()) {
23927 continue;
23928 } // only update if not display: none
23929
23930
23931 if (ele.pstyle('display').value === 'none') {
23932 continue;
23933 }
23934
23935 if (_p.group === 'nodes') {
23936 nodes.push(ele);
23937 } else {
23938 // edges
23939 edges.push(ele);
23940 }
23941
23942 rstyle.clean = true;
23943 } // update node data from projections
23944
23945
23946 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
23947 var _ele = nodes[_i2];
23948 var _p2 = _ele._private;
23949 var _rstyle = _p2.rstyle;
23950
23951 var pos = _ele.position();
23952
23953 this.recalculateNodeLabelProjection(_ele);
23954 _rstyle.nodeX = pos.x;
23955 _rstyle.nodeY = pos.y;
23956 _rstyle.nodeW = _ele.pstyle('width').pfValue;
23957 _rstyle.nodeH = _ele.pstyle('height').pfValue;
23958 }
23959
23960 this.recalculateEdgeProjections(edges); // update edge data from projections
23961
23962 for (var _i3 = 0; _i3 < edges.length; _i3++) {
23963 var _ele2 = edges[_i3];
23964 var _p3 = _ele2._private;
23965 var _rstyle2 = _p3.rstyle;
23966 var rs = _p3.rscratch; // update rstyle positions
23967
23968 _rstyle2.srcX = rs.arrowStartX;
23969 _rstyle2.srcY = rs.arrowStartY;
23970 _rstyle2.tgtX = rs.arrowEndX;
23971 _rstyle2.tgtY = rs.arrowEndY;
23972 _rstyle2.midX = rs.midX;
23973 _rstyle2.midY = rs.midY;
23974 _rstyle2.labelAngle = rs.labelAngle;
23975 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
23976 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
23977 }
23978};
23979
23980var BRp$9 = {};
23981
23982BRp$9.updateCachedGrabbedEles = function () {
23983 var eles = this.cachedZSortedEles;
23984
23985 if (!eles) {
23986 // just let this be recalculated on the next z sort tick
23987 return;
23988 }
23989
23990 eles.drag = [];
23991 eles.nondrag = [];
23992 var grabTargets = [];
23993
23994 for (var i = 0; i < eles.length; i++) {
23995 var ele = eles[i];
23996 var rs = ele._private.rscratch;
23997
23998 if (ele.grabbed() && !ele.isParent()) {
23999 grabTargets.push(ele);
24000 } else if (rs.inDragLayer) {
24001 eles.drag.push(ele);
24002 } else {
24003 eles.nondrag.push(ele);
24004 }
24005 } // put the grab target nodes last so it's on top of its neighbourhood
24006
24007
24008 for (var i = 0; i < grabTargets.length; i++) {
24009 var ele = grabTargets[i];
24010 eles.drag.push(ele);
24011 }
24012};
24013
24014BRp$9.invalidateCachedZSortedEles = function () {
24015 this.cachedZSortedEles = null;
24016};
24017
24018BRp$9.getCachedZSortedEles = function (forceRecalc) {
24019 if (forceRecalc || !this.cachedZSortedEles) {
24020 var eles = this.cy.mutableElements().toArray();
24021 eles.sort(zIndexSort);
24022 eles.interactive = eles.filter(function (ele) {
24023 return ele.interactive();
24024 });
24025 this.cachedZSortedEles = eles;
24026 this.updateCachedGrabbedEles();
24027 } else {
24028 eles = this.cachedZSortedEles;
24029 }
24030
24031 return eles;
24032};
24033
24034var BRp$a = {};
24035[BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
24036 extend(BRp$a, props);
24037});
24038
24039var BRp$b = {};
24040
24041BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
24042 var r = this;
24043 var imageCache = r.imageCache = r.imageCache || {};
24044 var cache = imageCache[url];
24045
24046 if (cache) {
24047 if (!cache.image.complete) {
24048 cache.image.addEventListener('load', onLoad);
24049 }
24050
24051 return cache.image;
24052 } else {
24053 cache = imageCache[url] = imageCache[url] || {};
24054 var image = cache.image = new Image(); // eslint-disable-line no-undef
24055
24056 image.addEventListener('load', onLoad);
24057 image.addEventListener('error', function () {
24058 image.error = true;
24059 }); // #1582 safari doesn't load data uris with crossOrigin properly
24060 // https://bugs.webkit.org/show_bug.cgi?id=123978
24061
24062 var dataUriPrefix = 'data:';
24063 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24064
24065 if (!isDataUri) {
24066 image.crossOrigin = crossOrigin; // prevent tainted canvas
24067 }
24068
24069 image.src = url;
24070 return image;
24071 }
24072};
24073
24074var BRp$c = {};
24075/* global document, window, ResizeObserver, MutationObserver */
24076
24077BRp$c.registerBinding = function (target, event, handler, useCapture) {
24078 // eslint-disable-line no-unused-vars
24079 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24080
24081 var b = this.binder(target);
24082 return b.on.apply(b, args);
24083};
24084
24085BRp$c.binder = function (tgt) {
24086 var r = this;
24087 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24088
24089 if (r.supportsPassiveEvents == null) {
24090 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24091 var supportsPassive = false;
24092
24093 try {
24094 var opts = Object.defineProperty({}, 'passive', {
24095 get: function get() {
24096 supportsPassive = true;
24097 return true;
24098 }
24099 });
24100 window.addEventListener('test', null, opts);
24101 } catch (err) {// not supported
24102 }
24103
24104 r.supportsPassiveEvents = supportsPassive;
24105 }
24106
24107 var on = function on(event, handler, useCapture) {
24108 var args = Array.prototype.slice.call(arguments);
24109
24110 if (tgtIsDom && r.supportsPassiveEvents) {
24111 // replace useCapture w/ opts obj
24112 args[2] = {
24113 capture: useCapture != null ? useCapture : false,
24114 passive: false,
24115 once: false
24116 };
24117 }
24118
24119 r.bindings.push({
24120 target: tgt,
24121 args: args
24122 });
24123 (tgt.addEventListener || tgt.on).apply(tgt, args);
24124 return this;
24125 };
24126
24127 return {
24128 on: on,
24129 addEventListener: on,
24130 addListener: on,
24131 bind: on
24132 };
24133};
24134
24135BRp$c.nodeIsDraggable = function (node) {
24136 return node && node.isNode() && !node.locked() && node.grabbable();
24137};
24138
24139BRp$c.nodeIsGrabbable = function (node) {
24140 return this.nodeIsDraggable(node) && node.interactive();
24141};
24142
24143BRp$c.load = function () {
24144 var r = this;
24145
24146 var isSelected = function isSelected(ele) {
24147 return ele.selected();
24148 };
24149
24150 var triggerEvents = function triggerEvents(target, names, e, position) {
24151 if (target == null) {
24152 target = r.cy;
24153 }
24154
24155 for (var i = 0; i < names.length; i++) {
24156 var name = names[i];
24157 target.emit({
24158 originalEvent: e,
24159 type: name,
24160 position: position
24161 });
24162 }
24163 };
24164
24165 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24166 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24167 };
24168
24169 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24170 var allowPassthrough = true;
24171
24172 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24173 // a grabbable compound node below the ele => no passthrough panning
24174 for (var i = 0; downs && i < downs.length; i++) {
24175 var down = downs[i]; //if any parent node in event hierarchy isn't pannable, reject passthrough
24176
24177 if (down.isNode() && down.isParent() && !down.pannable()) {
24178 allowPassthrough = false;
24179 break;
24180 }
24181 }
24182 } else {
24183 allowPassthrough = true;
24184 }
24185
24186 return allowPassthrough;
24187 };
24188
24189 var setGrabbed = function setGrabbed(ele) {
24190 ele[0]._private.grabbed = true;
24191 };
24192
24193 var setFreed = function setFreed(ele) {
24194 ele[0]._private.grabbed = false;
24195 };
24196
24197 var setInDragLayer = function setInDragLayer(ele) {
24198 ele[0]._private.rscratch.inDragLayer = true;
24199 };
24200
24201 var setOutDragLayer = function setOutDragLayer(ele) {
24202 ele[0]._private.rscratch.inDragLayer = false;
24203 };
24204
24205 var setGrabTarget = function setGrabTarget(ele) {
24206 ele[0]._private.rscratch.isGrabTarget = true;
24207 };
24208
24209 var removeGrabTarget = function removeGrabTarget(ele) {
24210 ele[0]._private.rscratch.isGrabTarget = false;
24211 };
24212
24213 var addToDragList = function addToDragList(ele, opts) {
24214 var list = opts.addToList;
24215 var listHasEle = list.has(ele);
24216
24217 if (!listHasEle) {
24218 list.merge(ele);
24219 setGrabbed(ele);
24220 }
24221 }; // helper function to determine which child nodes and inner edges
24222 // of a compound node to be dragged as well as the grabbed and selected nodes
24223
24224
24225 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24226 if (!node.cy().hasCompoundNodes()) {
24227 return;
24228 }
24229
24230 if (opts.inDragLayer == null && opts.addToList == null) {
24231 return;
24232 } // nothing to do
24233
24234
24235 var innerNodes = node.descendants();
24236
24237 if (opts.inDragLayer) {
24238 innerNodes.forEach(setInDragLayer);
24239 innerNodes.connectedEdges().forEach(setInDragLayer);
24240 }
24241
24242 if (opts.addToList) {
24243 opts.addToList.unmerge(innerNodes);
24244 }
24245 }; // adds the given nodes and its neighbourhood to the drag layer
24246
24247
24248 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24249 opts = opts || {};
24250 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24251
24252 if (opts.inDragLayer) {
24253 nodes.forEach(setInDragLayer);
24254 nodes.neighborhood().stdFilter(function (ele) {
24255 return !hasCompoundNodes || ele.isEdge();
24256 }).forEach(setInDragLayer);
24257 }
24258
24259 if (opts.addToList) {
24260 nodes.forEach(function (ele) {
24261 addToDragList(ele, opts);
24262 });
24263 }
24264
24265 addDescendantsToDrag(nodes, opts); // always add to drag
24266 // also add nodes and edges related to the topmost ancestor
24267
24268 updateAncestorsInDragLayer(nodes, {
24269 inDragLayer: opts.inDragLayer
24270 });
24271 r.updateCachedGrabbedEles();
24272 };
24273
24274 var addNodeToDrag = addNodesToDrag;
24275
24276 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24277 if (!grabbedEles) {
24278 return;
24279 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24280
24281
24282 r.getCachedZSortedEles().forEach(function (ele) {
24283 setFreed(ele);
24284 setOutDragLayer(ele);
24285 removeGrabTarget(ele);
24286 });
24287 r.updateCachedGrabbedEles();
24288 }; // helper function to determine which ancestor nodes and edges should go
24289 // to the drag layer (or should be removed from drag layer).
24290
24291
24292 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24293 if (opts.inDragLayer == null && opts.addToList == null) {
24294 return;
24295 } // nothing to do
24296
24297
24298 if (!node.cy().hasCompoundNodes()) {
24299 return;
24300 } // find top-level parent
24301
24302
24303 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
24304
24305 if (parent.same(node)) {
24306 return;
24307 }
24308
24309 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24310 var edges = nodes.connectedEdges();
24311
24312 if (opts.inDragLayer) {
24313 edges.forEach(setInDragLayer);
24314 nodes.forEach(setInDragLayer);
24315 }
24316
24317 if (opts.addToList) {
24318 nodes.forEach(function (ele) {
24319 addToDragList(ele, opts);
24320 });
24321 }
24322 };
24323
24324 var blurActiveDomElement = function blurActiveDomElement() {
24325 if (document.activeElement != null && document.activeElement.blur != null) {
24326 document.activeElement.blur();
24327 }
24328 };
24329
24330 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24331 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
24332
24333 if (haveMutationsApi) {
24334 r.removeObserver = new MutationObserver(function (mutns) {
24335 // eslint-disable-line no-undef
24336 for (var i = 0; i < mutns.length; i++) {
24337 var mutn = mutns[i];
24338 var rNodes = mutn.removedNodes;
24339
24340 if (rNodes) {
24341 for (var j = 0; j < rNodes.length; j++) {
24342 var rNode = rNodes[j];
24343
24344 if (rNode === r.container) {
24345 r.destroy();
24346 break;
24347 }
24348 }
24349 }
24350 }
24351 });
24352
24353 if (r.container.parentNode) {
24354 r.removeObserver.observe(r.container.parentNode, {
24355 childList: true
24356 });
24357 }
24358 } else {
24359 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24360 // eslint-disable-line no-unused-vars
24361 r.destroy();
24362 });
24363 }
24364
24365 var onResize = util(function () {
24366 r.cy.resize();
24367 }, 100);
24368
24369 if (haveMutationsApi) {
24370 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24371
24372 r.styleObserver.observe(r.container, {
24373 attributes: true
24374 });
24375 } // auto resize
24376
24377
24378 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
24379
24380 if (haveResizeObserverApi) {
24381 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24382
24383 r.resizeObserver.observe(r.container);
24384 }
24385
24386 var forEachUp = function forEachUp(domEle, fn) {
24387 while (domEle != null) {
24388 fn(domEle);
24389 domEle = domEle.parentNode;
24390 }
24391 };
24392
24393 var invalidateCoords = function invalidateCoords() {
24394 r.invalidateContainerClientCoordsCache();
24395 };
24396
24397 forEachUp(r.container, function (domEle) {
24398 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24399 r.registerBinding(domEle, 'animationend', invalidateCoords);
24400 r.registerBinding(domEle, 'scroll', invalidateCoords);
24401 }); // stop right click menu from appearing on cy
24402
24403 r.registerBinding(r.container, 'contextmenu', function (e) {
24404 e.preventDefault();
24405 });
24406
24407 var inBoxSelection = function inBoxSelection() {
24408 return r.selection[4] !== 0;
24409 };
24410
24411 var eventInContainer = function eventInContainer(e) {
24412 // save cycles if mouse events aren't to be captured
24413 var containerPageCoords = r.findContainerClientCoords();
24414 var x = containerPageCoords[0];
24415 var y = containerPageCoords[1];
24416 var width = containerPageCoords[2];
24417 var height = containerPageCoords[3];
24418 var positions = e.touches ? e.touches : [e];
24419 var atLeastOnePosInside = false;
24420
24421 for (var i = 0; i < positions.length; i++) {
24422 var p = positions[i];
24423
24424 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24425 atLeastOnePosInside = true;
24426 break;
24427 }
24428 }
24429
24430 if (!atLeastOnePosInside) {
24431 return false;
24432 }
24433
24434 var container = r.container;
24435 var target = e.target;
24436 var tParent = target.parentNode;
24437 var containerIsTarget = false;
24438
24439 while (tParent) {
24440 if (tParent === container) {
24441 containerIsTarget = true;
24442 break;
24443 }
24444
24445 tParent = tParent.parentNode;
24446 }
24447
24448 if (!containerIsTarget) {
24449 return false;
24450 } // if target is outisde cy container, then this event is not for us
24451
24452
24453 return true;
24454 }; // Primary key
24455
24456
24457 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24458 if (!eventInContainer(e)) {
24459 return;
24460 }
24461
24462 e.preventDefault();
24463 blurActiveDomElement();
24464 r.hoverData.capture = true;
24465 r.hoverData.which = e.which;
24466 var cy = r.cy;
24467 var gpos = [e.clientX, e.clientY];
24468 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24469 var select = r.selection;
24470 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24471 var near = nears[0];
24472 var draggedElements = r.dragData.possibleDragElements;
24473 r.hoverData.mdownPos = pos;
24474 r.hoverData.mdownGPos = gpos;
24475
24476 var checkForTaphold = function checkForTaphold() {
24477 r.hoverData.tapholdCancelled = false;
24478 clearTimeout(r.hoverData.tapholdTimeout);
24479 r.hoverData.tapholdTimeout = setTimeout(function () {
24480 if (r.hoverData.tapholdCancelled) {
24481 return;
24482 } else {
24483 var ele = r.hoverData.down;
24484
24485 if (ele) {
24486 ele.emit({
24487 originalEvent: e,
24488 type: 'taphold',
24489 position: {
24490 x: pos[0],
24491 y: pos[1]
24492 }
24493 });
24494 } else {
24495 cy.emit({
24496 originalEvent: e,
24497 type: 'taphold',
24498 position: {
24499 x: pos[0],
24500 y: pos[1]
24501 }
24502 });
24503 }
24504 }
24505 }, r.tapholdDuration);
24506 }; // Right click button
24507
24508
24509 if (e.which == 3) {
24510 r.hoverData.cxtStarted = true;
24511 var cxtEvt = {
24512 originalEvent: e,
24513 type: 'cxttapstart',
24514 position: {
24515 x: pos[0],
24516 y: pos[1]
24517 }
24518 };
24519
24520 if (near) {
24521 near.activate();
24522 near.emit(cxtEvt);
24523 r.hoverData.down = near;
24524 } else {
24525 cy.emit(cxtEvt);
24526 }
24527
24528 r.hoverData.downTime = new Date().getTime();
24529 r.hoverData.cxtDragged = false; // Primary button
24530 } else if (e.which == 1) {
24531 if (near) {
24532 near.activate();
24533 } // Element dragging
24534
24535
24536 {
24537 // If something is under the cursor and it is draggable, prepare to grab it
24538 if (near != null) {
24539 if (r.nodeIsGrabbable(near)) {
24540 var makeEvent = function makeEvent(type) {
24541 return {
24542 originalEvent: e,
24543 type: type,
24544 position: {
24545 x: pos[0],
24546 y: pos[1]
24547 }
24548 };
24549 };
24550
24551 var triggerGrab = function triggerGrab(ele) {
24552 ele.emit(makeEvent('grab'));
24553 };
24554
24555 setGrabTarget(near);
24556
24557 if (!near.selected()) {
24558 draggedElements = r.dragData.possibleDragElements = cy.collection();
24559 addNodeToDrag(near, {
24560 addToList: draggedElements
24561 });
24562 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24563 } else {
24564 draggedElements = r.dragData.possibleDragElements = cy.collection();
24565 var selectedNodes = cy.$(function (ele) {
24566 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24567 });
24568 addNodesToDrag(selectedNodes, {
24569 addToList: draggedElements
24570 });
24571 near.emit(makeEvent('grabon'));
24572 selectedNodes.forEach(triggerGrab);
24573 }
24574
24575 r.redrawHint('eles', true);
24576 r.redrawHint('drag', true);
24577 }
24578 }
24579
24580 r.hoverData.down = near;
24581 r.hoverData.downs = nears;
24582 r.hoverData.downTime = new Date().getTime();
24583 }
24584 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24585 x: pos[0],
24586 y: pos[1]
24587 });
24588
24589 if (near == null) {
24590 select[4] = 1;
24591 r.data.bgActivePosistion = {
24592 x: pos[0],
24593 y: pos[1]
24594 };
24595 r.redrawHint('select', true);
24596 r.redraw();
24597 } else if (near.pannable()) {
24598 select[4] = 1; // for future pan
24599 }
24600
24601 checkForTaphold();
24602 } // Initialize selection box coordinates
24603
24604
24605 select[0] = select[2] = pos[0];
24606 select[1] = select[3] = pos[1];
24607 }, false);
24608 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
24609 // eslint-disable-line no-undef
24610 var capture = r.hoverData.capture;
24611
24612 if (!capture && !eventInContainer(e)) {
24613 return;
24614 }
24615
24616 var preventDefault = false;
24617 var cy = r.cy;
24618 var zoom = cy.zoom();
24619 var gpos = [e.clientX, e.clientY];
24620 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24621 var mdownPos = r.hoverData.mdownPos;
24622 var mdownGPos = r.hoverData.mdownGPos;
24623 var select = r.selection;
24624 var near = null;
24625
24626 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24627 near = r.findNearestElement(pos[0], pos[1], true, false);
24628 }
24629
24630 var last = r.hoverData.last;
24631 var down = r.hoverData.down;
24632 var disp = [pos[0] - select[2], pos[1] - select[3]];
24633 var draggedElements = r.dragData.possibleDragElements;
24634 var isOverThresholdDrag;
24635
24636 if (mdownGPos) {
24637 var dx = gpos[0] - mdownGPos[0];
24638 var dx2 = dx * dx;
24639 var dy = gpos[1] - mdownGPos[1];
24640 var dy2 = dy * dy;
24641 var dist2 = dx2 + dy2;
24642 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24643 }
24644
24645 var multSelKeyDown = isMultSelKeyDown(e);
24646
24647 if (isOverThresholdDrag) {
24648 r.hoverData.tapholdCancelled = true;
24649 }
24650
24651 var updateDragDelta = function updateDragDelta() {
24652 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24653
24654 if (dragDelta.length === 0) {
24655 dragDelta.push(disp[0]);
24656 dragDelta.push(disp[1]);
24657 } else {
24658 dragDelta[0] += disp[0];
24659 dragDelta[1] += disp[1];
24660 }
24661 };
24662
24663 preventDefault = true;
24664 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24665 x: pos[0],
24666 y: pos[1]
24667 });
24668
24669 var goIntoBoxMode = function goIntoBoxMode() {
24670 r.data.bgActivePosistion = undefined;
24671
24672 if (!r.hoverData.selecting) {
24673 cy.emit({
24674 originalEvent: e,
24675 type: 'boxstart',
24676 position: {
24677 x: pos[0],
24678 y: pos[1]
24679 }
24680 });
24681 }
24682
24683 select[4] = 1;
24684 r.hoverData.selecting = true;
24685 r.redrawHint('select', true);
24686 r.redraw();
24687 }; // trigger context drag if rmouse down
24688
24689
24690 if (r.hoverData.which === 3) {
24691 // but only if over threshold
24692 if (isOverThresholdDrag) {
24693 var cxtEvt = {
24694 originalEvent: e,
24695 type: 'cxtdrag',
24696 position: {
24697 x: pos[0],
24698 y: pos[1]
24699 }
24700 };
24701
24702 if (down) {
24703 down.emit(cxtEvt);
24704 } else {
24705 cy.emit(cxtEvt);
24706 }
24707
24708 r.hoverData.cxtDragged = true;
24709
24710 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24711 if (r.hoverData.cxtOver) {
24712 r.hoverData.cxtOver.emit({
24713 originalEvent: e,
24714 type: 'cxtdragout',
24715 position: {
24716 x: pos[0],
24717 y: pos[1]
24718 }
24719 });
24720 }
24721
24722 r.hoverData.cxtOver = near;
24723
24724 if (near) {
24725 near.emit({
24726 originalEvent: e,
24727 type: 'cxtdragover',
24728 position: {
24729 x: pos[0],
24730 y: pos[1]
24731 }
24732 });
24733 }
24734 }
24735 } // Check if we are drag panning the entire graph
24736
24737 } else if (r.hoverData.dragging) {
24738 preventDefault = true;
24739
24740 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24741 var deltaP;
24742
24743 if (r.hoverData.justStartedPan) {
24744 var mdPos = r.hoverData.mdownPos;
24745 deltaP = {
24746 x: (pos[0] - mdPos[0]) * zoom,
24747 y: (pos[1] - mdPos[1]) * zoom
24748 };
24749 r.hoverData.justStartedPan = false;
24750 } else {
24751 deltaP = {
24752 x: disp[0] * zoom,
24753 y: disp[1] * zoom
24754 };
24755 }
24756
24757 cy.panBy(deltaP);
24758 cy.emit('dragpan');
24759 r.hoverData.dragged = true;
24760 } // Needs reproject due to pan changing viewport
24761
24762
24763 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24764 } else if (select[4] == 1 && (down == null || down.pannable())) {
24765 if (isOverThresholdDrag) {
24766 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24767 goIntoBoxMode();
24768 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24769 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24770
24771 if (allowPassthrough) {
24772 r.hoverData.dragging = true;
24773 r.hoverData.justStartedPan = true;
24774 select[4] = 0;
24775 r.data.bgActivePosistion = array2point(mdownPos);
24776 r.redrawHint('select', true);
24777 r.redraw();
24778 }
24779 }
24780
24781 if (down && down.pannable() && down.active()) {
24782 down.unactivate();
24783 }
24784 }
24785 } else {
24786 if (down && down.pannable() && down.active()) {
24787 down.unactivate();
24788 }
24789
24790 if ((!down || !down.grabbed()) && near != last) {
24791 if (last) {
24792 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24793 x: pos[0],
24794 y: pos[1]
24795 });
24796 }
24797
24798 if (near) {
24799 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24800 x: pos[0],
24801 y: pos[1]
24802 });
24803 }
24804
24805 r.hoverData.last = near;
24806 }
24807
24808 if (down) {
24809 if (isOverThresholdDrag) {
24810 // then we can take action
24811 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24812 // then selection overrides
24813 if (down && down.grabbed()) {
24814 freeDraggedElements(draggedElements);
24815 down.emit('freeon');
24816 draggedElements.emit('free');
24817
24818 if (r.dragData.didDrag) {
24819 down.emit('dragfreeon');
24820 draggedElements.emit('dragfree');
24821 }
24822 }
24823
24824 goIntoBoxMode();
24825 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24826 // drag node
24827 var justStartedDrag = !r.dragData.didDrag;
24828
24829 if (justStartedDrag) {
24830 r.redrawHint('eles', true);
24831 }
24832
24833 r.dragData.didDrag = true; // indicate that we actually did drag the node
24834
24835 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
24836
24837 if (!r.hoverData.draggingEles) {
24838 addNodesToDrag(draggedElements, {
24839 inDragLayer: true
24840 });
24841 }
24842
24843 var totalShift = {
24844 x: 0,
24845 y: 0
24846 };
24847
24848 if (number(disp[0]) && number(disp[1])) {
24849 totalShift.x += disp[0];
24850 totalShift.y += disp[1];
24851
24852 if (justStartedDrag) {
24853 var dragDelta = r.hoverData.dragDelta;
24854
24855 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
24856 totalShift.x += dragDelta[0];
24857 totalShift.y += dragDelta[1];
24858 }
24859 }
24860 }
24861
24862 for (var i = 0; i < draggedElements.length; i++) {
24863 var dEle = draggedElements[i];
24864
24865 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
24866 toTrigger.push(dEle);
24867 }
24868 }
24869
24870 r.hoverData.draggingEles = true;
24871 toTrigger.silentShift(totalShift).emit('position drag');
24872 r.redrawHint('drag', true);
24873 r.redraw();
24874 }
24875 } else {
24876 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
24877 updateDragDelta();
24878 }
24879 } // prevent the dragging from triggering text selection on the page
24880
24881
24882 preventDefault = true;
24883 }
24884
24885 select[2] = pos[0];
24886 select[3] = pos[1];
24887
24888 if (preventDefault) {
24889 if (e.stopPropagation) e.stopPropagation();
24890 if (e.preventDefault) e.preventDefault();
24891 return false;
24892 }
24893 }, false);
24894 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
24895 // eslint-disable-line no-undef
24896 var capture = r.hoverData.capture;
24897
24898 if (!capture) {
24899 return;
24900 }
24901
24902 r.hoverData.capture = false;
24903 var cy = r.cy;
24904 var pos = r.projectIntoViewport(e.clientX, e.clientY);
24905 var select = r.selection;
24906 var near = r.findNearestElement(pos[0], pos[1], true, false);
24907 var draggedElements = r.dragData.possibleDragElements;
24908 var down = r.hoverData.down;
24909 var multSelKeyDown = isMultSelKeyDown(e);
24910
24911 if (r.data.bgActivePosistion) {
24912 r.redrawHint('select', true);
24913 r.redraw();
24914 }
24915
24916 r.hoverData.tapholdCancelled = true;
24917 r.data.bgActivePosistion = undefined; // not active bg now
24918
24919 if (down) {
24920 down.unactivate();
24921 }
24922
24923 if (r.hoverData.which === 3) {
24924 var cxtEvt = {
24925 originalEvent: e,
24926 type: 'cxttapend',
24927 position: {
24928 x: pos[0],
24929 y: pos[1]
24930 }
24931 };
24932
24933 if (down) {
24934 down.emit(cxtEvt);
24935 } else {
24936 cy.emit(cxtEvt);
24937 }
24938
24939 if (!r.hoverData.cxtDragged) {
24940 var cxtTap = {
24941 originalEvent: e,
24942 type: 'cxttap',
24943 position: {
24944 x: pos[0],
24945 y: pos[1]
24946 }
24947 };
24948
24949 if (down) {
24950 down.emit(cxtTap);
24951 } else {
24952 cy.emit(cxtTap);
24953 }
24954 }
24955
24956 r.hoverData.cxtDragged = false;
24957 r.hoverData.which = null;
24958 } else if (r.hoverData.which === 1) {
24959 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
24960 x: pos[0],
24961 y: pos[1]
24962 });
24963
24964 if (!r.dragData.didDrag // didn't move a node around
24965 && !r.hoverData.dragged // didn't pan
24966 && !r.hoverData.selecting // not box selection
24967 && !r.hoverData.isOverThresholdDrag // didn't move too much
24968 ) {
24969 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
24970 x: pos[0],
24971 y: pos[1]
24972 });
24973 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
24974
24975
24976 if (down == null && // not mousedown on node
24977 !r.dragData.didDrag // didn't move the node around
24978 && !r.hoverData.selecting // not box selection
24979 && !r.hoverData.dragged // didn't pan
24980 && !isMultSelKeyDown(e)) {
24981 cy.$(isSelected).unselect(['tapunselect']);
24982
24983 if (draggedElements.length > 0) {
24984 r.redrawHint('eles', true);
24985 }
24986
24987 r.dragData.possibleDragElements = draggedElements = cy.collection();
24988 } // Single selection
24989
24990
24991 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
24992 if (near != null && near._private.selectable) {
24993 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
24994 if (near.selected()) {
24995 near.unselect(['tapunselect']);
24996 } else {
24997 near.select(['tapselect']);
24998 }
24999 } else {
25000 if (!multSelKeyDown) {
25001 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25002 near.select(['tapselect']);
25003 }
25004 }
25005
25006 r.redrawHint('eles', true);
25007 }
25008 }
25009
25010 if (r.hoverData.selecting) {
25011 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25012 r.redrawHint('select', true);
25013
25014 if (box.length > 0) {
25015 r.redrawHint('eles', true);
25016 }
25017
25018 cy.emit({
25019 type: 'boxend',
25020 originalEvent: e,
25021 position: {
25022 x: pos[0],
25023 y: pos[1]
25024 }
25025 });
25026
25027 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25028 return ele.selectable() && !ele.selected();
25029 };
25030
25031 if (cy.selectionType() === 'additive') {
25032 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25033 } else {
25034 if (!multSelKeyDown) {
25035 cy.$(isSelected).unmerge(box).unselect();
25036 }
25037
25038 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25039 } // always need redraw in case eles unselectable
25040
25041
25042 r.redraw();
25043 } // Cancel drag pan
25044
25045
25046 if (r.hoverData.dragging) {
25047 r.hoverData.dragging = false;
25048 r.redrawHint('select', true);
25049 r.redrawHint('eles', true);
25050 r.redraw();
25051 }
25052
25053 if (!select[4]) {
25054 r.redrawHint('drag', true);
25055 r.redrawHint('eles', true);
25056 var downWasGrabbed = down && down.grabbed();
25057 freeDraggedElements(draggedElements);
25058
25059 if (downWasGrabbed) {
25060 down.emit('freeon');
25061 draggedElements.emit('free');
25062
25063 if (r.dragData.didDrag) {
25064 down.emit('dragfreeon');
25065 draggedElements.emit('dragfree');
25066 }
25067 }
25068 }
25069 } // else not right mouse
25070
25071
25072 select[4] = 0;
25073 r.hoverData.down = null;
25074 r.hoverData.cxtStarted = false;
25075 r.hoverData.draggingEles = false;
25076 r.hoverData.selecting = false;
25077 r.hoverData.isOverThresholdDrag = false;
25078 r.dragData.didDrag = false;
25079 r.hoverData.dragged = false;
25080 r.hoverData.dragDelta = [];
25081 r.hoverData.mdownPos = null;
25082 r.hoverData.mdownGPos = null;
25083 }, false);
25084
25085 var wheelHandler = function wheelHandler(e) {
25086 if (r.scrollingPage) {
25087 return;
25088 } // while scrolling, ignore wheel-to-zoom
25089
25090
25091 var cy = r.cy;
25092 var zoom = cy.zoom();
25093 var pan = cy.pan();
25094 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25095 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25096
25097 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25098 // if pan dragging or cxt dragging, wheel movements make no zoom
25099 e.preventDefault();
25100 return;
25101 }
25102
25103 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25104 e.preventDefault();
25105 r.data.wheelZooming = true;
25106 clearTimeout(r.data.wheelTimeout);
25107 r.data.wheelTimeout = setTimeout(function () {
25108 r.data.wheelZooming = false;
25109 r.redrawHint('eles', true);
25110 r.redraw();
25111 }, 150);
25112 var diff;
25113
25114 if (e.deltaY != null) {
25115 diff = e.deltaY / -250;
25116 } else if (e.wheelDeltaY != null) {
25117 diff = e.wheelDeltaY / 1000;
25118 } else {
25119 diff = e.wheelDelta / 1000;
25120 }
25121
25122 diff = diff * r.wheelSensitivity;
25123 var needsWheelFix = e.deltaMode === 1;
25124
25125 if (needsWheelFix) {
25126 // fixes slow wheel events on ff/linux and ff/windows
25127 diff *= 33;
25128 }
25129
25130 var newZoom = cy.zoom() * Math.pow(10, diff);
25131
25132 if (e.type === 'gesturechange') {
25133 newZoom = r.gestureStartZoom * e.scale;
25134 }
25135
25136 cy.zoom({
25137 level: newZoom,
25138 renderedPosition: {
25139 x: rpos[0],
25140 y: rpos[1]
25141 }
25142 });
25143 cy.emit(e.type === 'gesturechange' ? 'pinchzoom' : 'scrollzoom');
25144 }
25145 }; // Functions to help with whether mouse wheel should trigger zooming
25146 // --
25147
25148
25149 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25150 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25151 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25152 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25153
25154 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25155 // eslint-disable-line no-unused-vars
25156 r.scrollingPage = true;
25157 clearTimeout(r.scrollingPageTimeout);
25158 r.scrollingPageTimeout = setTimeout(function () {
25159 r.scrollingPage = false;
25160 }, 250);
25161 }, true); // desktop safari pinch to zoom start
25162
25163 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25164 r.gestureStartZoom = r.cy.zoom();
25165
25166 if (!r.hasTouchStarted) {
25167 // don't affect touch devices like iphone
25168 e.preventDefault();
25169 }
25170 }, true);
25171 r.registerBinding(r.container, 'gesturechange', function (e) {
25172 if (!r.hasTouchStarted) {
25173 // don't affect touch devices like iphone
25174 wheelHandler(e);
25175 }
25176 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25177 // Handle mouseout on Cytoscape container
25178
25179 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25180 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25181 r.cy.emit({
25182 originalEvent: e,
25183 type: 'mouseout',
25184 position: {
25185 x: pos[0],
25186 y: pos[1]
25187 }
25188 });
25189 }, false);
25190 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25191 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25192 r.cy.emit({
25193 originalEvent: e,
25194 type: 'mouseover',
25195 position: {
25196 x: pos[0],
25197 y: pos[1]
25198 }
25199 });
25200 }, false);
25201 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25202
25203 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25204
25205 var center1, modelCenter1; // center point on start pinch to zoom
25206
25207 var offsetLeft, offsetTop;
25208 var containerWidth, containerHeight;
25209 var twoFingersStartInside;
25210
25211 var distance = function distance(x1, y1, x2, y2) {
25212 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25213 };
25214
25215 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25216 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25217 };
25218
25219 var touchstartHandler;
25220 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25221 r.hasTouchStarted = true;
25222
25223 if (!eventInContainer(e)) {
25224 return;
25225 }
25226
25227 blurActiveDomElement();
25228 r.touchData.capture = true;
25229 r.data.bgActivePosistion = undefined;
25230 var cy = r.cy;
25231 var now = r.touchData.now;
25232 var earlier = r.touchData.earlier;
25233
25234 if (e.touches[0]) {
25235 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25236 now[0] = pos[0];
25237 now[1] = pos[1];
25238 }
25239
25240 if (e.touches[1]) {
25241 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25242 now[2] = pos[0];
25243 now[3] = pos[1];
25244 }
25245
25246 if (e.touches[2]) {
25247 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25248 now[4] = pos[0];
25249 now[5] = pos[1];
25250 } // record starting points for pinch-to-zoom
25251
25252
25253 if (e.touches[1]) {
25254 r.touchData.singleTouchMoved = true;
25255 freeDraggedElements(r.dragData.touchDragEles);
25256 var offsets = r.findContainerClientCoords();
25257 offsetLeft = offsets[0];
25258 offsetTop = offsets[1];
25259 containerWidth = offsets[2];
25260 containerHeight = offsets[3];
25261 f1x1 = e.touches[0].clientX - offsetLeft;
25262 f1y1 = e.touches[0].clientY - offsetTop;
25263 f2x1 = e.touches[1].clientX - offsetLeft;
25264 f2y1 = e.touches[1].clientY - offsetTop;
25265 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25266 var pan = cy.pan();
25267 var zoom = cy.zoom();
25268 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25269 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25270 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25271 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25272
25273 var cxtDistThreshold = 200;
25274 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25275
25276 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25277 var near1 = r.findNearestElement(now[0], now[1], true, true);
25278 var near2 = r.findNearestElement(now[2], now[3], true, true);
25279
25280 if (near1 && near1.isNode()) {
25281 near1.activate().emit({
25282 originalEvent: e,
25283 type: 'cxttapstart',
25284 position: {
25285 x: now[0],
25286 y: now[1]
25287 }
25288 });
25289 r.touchData.start = near1;
25290 } else if (near2 && near2.isNode()) {
25291 near2.activate().emit({
25292 originalEvent: e,
25293 type: 'cxttapstart',
25294 position: {
25295 x: now[0],
25296 y: now[1]
25297 }
25298 });
25299 r.touchData.start = near2;
25300 } else {
25301 cy.emit({
25302 originalEvent: e,
25303 type: 'cxttapstart',
25304 position: {
25305 x: now[0],
25306 y: now[1]
25307 }
25308 });
25309 }
25310
25311 if (r.touchData.start) {
25312 r.touchData.start._private.grabbed = false;
25313 }
25314
25315 r.touchData.cxt = true;
25316 r.touchData.cxtDragged = false;
25317 r.data.bgActivePosistion = undefined;
25318 r.redraw();
25319 return;
25320 }
25321 }
25322
25323 if (e.touches[2]) {
25324 // ignore
25325 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25326 if (cy.boxSelectionEnabled()) {
25327 e.preventDefault();
25328 }
25329 } else if (e.touches[1]) ; else if (e.touches[0]) {
25330 var nears = r.findNearestElements(now[0], now[1], true, true);
25331 var near = nears[0];
25332
25333 if (near != null) {
25334 near.activate();
25335 r.touchData.start = near;
25336 r.touchData.starts = nears;
25337
25338 if (r.nodeIsGrabbable(near)) {
25339 var draggedEles = r.dragData.touchDragEles = cy.collection();
25340 var selectedNodes = null;
25341 r.redrawHint('eles', true);
25342 r.redrawHint('drag', true);
25343
25344 if (near.selected()) {
25345 // reset drag elements, since near will be added again
25346 selectedNodes = cy.$(function (ele) {
25347 return ele.selected() && r.nodeIsGrabbable(ele);
25348 });
25349 addNodesToDrag(selectedNodes, {
25350 addToList: draggedEles
25351 });
25352 } else {
25353 addNodeToDrag(near, {
25354 addToList: draggedEles
25355 });
25356 }
25357
25358 setGrabTarget(near);
25359
25360 var makeEvent = function makeEvent(type) {
25361 return {
25362 originalEvent: e,
25363 type: type,
25364 position: {
25365 x: now[0],
25366 y: now[1]
25367 }
25368 };
25369 };
25370
25371 near.emit(makeEvent('grabon'));
25372
25373 if (selectedNodes) {
25374 selectedNodes.forEach(function (n) {
25375 n.emit(makeEvent('grab'));
25376 });
25377 } else {
25378 near.emit(makeEvent('grab'));
25379 }
25380 }
25381 }
25382
25383 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25384 x: now[0],
25385 y: now[1]
25386 });
25387
25388 if (near == null) {
25389 r.data.bgActivePosistion = {
25390 x: pos[0],
25391 y: pos[1]
25392 };
25393 r.redrawHint('select', true);
25394 r.redraw();
25395 } // Tap, taphold
25396 // -----
25397
25398
25399 r.touchData.singleTouchMoved = false;
25400 r.touchData.singleTouchStartTime = +new Date();
25401 clearTimeout(r.touchData.tapholdTimeout);
25402 r.touchData.tapholdTimeout = setTimeout(function () {
25403 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25404 && !r.touchData.selecting // box selection shouldn't allow taphold through
25405 ) {
25406 triggerEvents(r.touchData.start, ['taphold'], e, {
25407 x: now[0],
25408 y: now[1]
25409 });
25410 }
25411 }, r.tapholdDuration);
25412 }
25413
25414 if (e.touches.length >= 1) {
25415 var sPos = r.touchData.startPosition = [];
25416
25417 for (var i = 0; i < now.length; i++) {
25418 sPos[i] = earlier[i] = now[i];
25419 }
25420
25421 var touch0 = e.touches[0];
25422 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25423 }
25424 }, false);
25425 var touchmoveHandler;
25426 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25427 // eslint-disable-line no-undef
25428 var capture = r.touchData.capture;
25429
25430 if (!capture && !eventInContainer(e)) {
25431 return;
25432 }
25433
25434 var select = r.selection;
25435 var cy = r.cy;
25436 var now = r.touchData.now;
25437 var earlier = r.touchData.earlier;
25438 var zoom = cy.zoom();
25439
25440 if (e.touches[0]) {
25441 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25442 now[0] = pos[0];
25443 now[1] = pos[1];
25444 }
25445
25446 if (e.touches[1]) {
25447 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25448 now[2] = pos[0];
25449 now[3] = pos[1];
25450 }
25451
25452 if (e.touches[2]) {
25453 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25454 now[4] = pos[0];
25455 now[5] = pos[1];
25456 }
25457
25458 var startGPos = r.touchData.startGPosition;
25459 var isOverThresholdDrag;
25460
25461 if (capture && e.touches[0] && startGPos) {
25462 var disp = [];
25463
25464 for (var j = 0; j < now.length; j++) {
25465 disp[j] = now[j] - earlier[j];
25466 }
25467
25468 var dx = e.touches[0].clientX - startGPos[0];
25469 var dx2 = dx * dx;
25470 var dy = e.touches[0].clientY - startGPos[1];
25471 var dy2 = dy * dy;
25472 var dist2 = dx2 + dy2;
25473 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25474 } // context swipe cancelling
25475
25476
25477 if (capture && r.touchData.cxt) {
25478 e.preventDefault();
25479 var f1x2 = e.touches[0].clientX - offsetLeft,
25480 f1y2 = e.touches[0].clientY - offsetTop;
25481 var f2x2 = e.touches[1].clientX - offsetLeft,
25482 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25483
25484 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25485 var factorSq = distance2Sq / distance1Sq;
25486 var distThreshold = 150;
25487 var distThresholdSq = distThreshold * distThreshold;
25488 var factorThreshold = 1.5;
25489 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25490
25491 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25492 r.touchData.cxt = false;
25493 r.data.bgActivePosistion = undefined;
25494 r.redrawHint('select', true);
25495 var cxtEvt = {
25496 originalEvent: e,
25497 type: 'cxttapend',
25498 position: {
25499 x: now[0],
25500 y: now[1]
25501 }
25502 };
25503
25504 if (r.touchData.start) {
25505 r.touchData.start.unactivate().emit(cxtEvt);
25506 r.touchData.start = null;
25507 } else {
25508 cy.emit(cxtEvt);
25509 }
25510 }
25511 } // context swipe
25512
25513
25514 if (capture && r.touchData.cxt) {
25515 var cxtEvt = {
25516 originalEvent: e,
25517 type: 'cxtdrag',
25518 position: {
25519 x: now[0],
25520 y: now[1]
25521 }
25522 };
25523 r.data.bgActivePosistion = undefined;
25524 r.redrawHint('select', true);
25525
25526 if (r.touchData.start) {
25527 r.touchData.start.emit(cxtEvt);
25528 } else {
25529 cy.emit(cxtEvt);
25530 }
25531
25532 if (r.touchData.start) {
25533 r.touchData.start._private.grabbed = false;
25534 }
25535
25536 r.touchData.cxtDragged = true;
25537 var near = r.findNearestElement(now[0], now[1], true, true);
25538
25539 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25540 if (r.touchData.cxtOver) {
25541 r.touchData.cxtOver.emit({
25542 originalEvent: e,
25543 type: 'cxtdragout',
25544 position: {
25545 x: now[0],
25546 y: now[1]
25547 }
25548 });
25549 }
25550
25551 r.touchData.cxtOver = near;
25552
25553 if (near) {
25554 near.emit({
25555 originalEvent: e,
25556 type: 'cxtdragover',
25557 position: {
25558 x: now[0],
25559 y: now[1]
25560 }
25561 });
25562 }
25563 } // box selection
25564
25565 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25566 e.preventDefault();
25567 r.data.bgActivePosistion = undefined;
25568 this.lastThreeTouch = +new Date();
25569
25570 if (!r.touchData.selecting) {
25571 cy.emit({
25572 originalEvent: e,
25573 type: 'boxstart',
25574 position: {
25575 x: now[0],
25576 y: now[1]
25577 }
25578 });
25579 }
25580
25581 r.touchData.selecting = true;
25582 r.touchData.didSelect = true;
25583 select[4] = 1;
25584
25585 if (!select || select.length === 0 || select[0] === undefined) {
25586 select[0] = (now[0] + now[2] + now[4]) / 3;
25587 select[1] = (now[1] + now[3] + now[5]) / 3;
25588 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25589 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25590 } else {
25591 select[2] = (now[0] + now[2] + now[4]) / 3;
25592 select[3] = (now[1] + now[3] + now[5]) / 3;
25593 }
25594
25595 r.redrawHint('select', true);
25596 r.redraw(); // pinch to zoom
25597 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25598 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25599 // two fingers => pinch to zoom
25600 e.preventDefault();
25601 r.data.bgActivePosistion = undefined;
25602 r.redrawHint('select', true);
25603 var draggedEles = r.dragData.touchDragEles;
25604
25605 if (draggedEles) {
25606 r.redrawHint('drag', true);
25607
25608 for (var i = 0; i < draggedEles.length; i++) {
25609 var de_p = draggedEles[i]._private;
25610 de_p.grabbed = false;
25611 de_p.rscratch.inDragLayer = false;
25612 }
25613 }
25614
25615 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25616
25617 var f1x2 = e.touches[0].clientX - offsetLeft,
25618 f1y2 = e.touches[0].clientY - offsetTop;
25619 var f2x2 = e.touches[1].clientX - offsetLeft,
25620 f2y2 = e.touches[1].clientY - offsetTop;
25621 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25622 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25623
25624 var factor = distance2 / distance1;
25625
25626 if (twoFingersStartInside) {
25627 // delta finger1
25628 var df1x = f1x2 - f1x1;
25629 var df1y = f1y2 - f1y1; // delta finger 2
25630
25631 var df2x = f2x2 - f2x1;
25632 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25633 // i.e. so pinching cancels out and moving together pans
25634
25635 var tx = (df1x + df2x) / 2;
25636 var ty = (df1y + df2y) / 2; // now calculate the zoom
25637
25638 var zoom1 = cy.zoom();
25639 var zoom2 = zoom1 * factor;
25640 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25641
25642 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25643 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25644 var pan2 = {
25645 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25646 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25647 }; // remove dragged eles
25648
25649 if (_start && _start.active()) {
25650 var draggedEles = r.dragData.touchDragEles;
25651 freeDraggedElements(draggedEles);
25652 r.redrawHint('drag', true);
25653 r.redrawHint('eles', true);
25654
25655 _start.unactivate().emit('freeon');
25656
25657 draggedEles.emit('free');
25658
25659 if (r.dragData.didDrag) {
25660 _start.emit('dragfreeon');
25661
25662 draggedEles.emit('dragfree');
25663 }
25664 }
25665
25666 cy.viewport({
25667 zoom: zoom2,
25668 pan: pan2,
25669 cancelOnFailedZoom: true
25670 });
25671 cy.emit('pinchzoom');
25672 distance1 = distance2;
25673 f1x1 = f1x2;
25674 f1y1 = f1y2;
25675 f2x1 = f2x2;
25676 f2y1 = f2y2;
25677 r.pinching = true;
25678 } // Re-project
25679
25680
25681 if (e.touches[0]) {
25682 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25683 now[0] = pos[0];
25684 now[1] = pos[1];
25685 }
25686
25687 if (e.touches[1]) {
25688 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25689 now[2] = pos[0];
25690 now[3] = pos[1];
25691 }
25692
25693 if (e.touches[2]) {
25694 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25695 now[4] = pos[0];
25696 now[5] = pos[1];
25697 }
25698 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25699 ) {
25700 var start = r.touchData.start;
25701 var last = r.touchData.last;
25702 var near;
25703
25704 if (!r.hoverData.draggingEles && !r.swipePanning) {
25705 near = r.findNearestElement(now[0], now[1], true, true);
25706 }
25707
25708 if (capture && start != null) {
25709 e.preventDefault();
25710 } // dragging nodes
25711
25712
25713 if (capture && start != null && r.nodeIsDraggable(start)) {
25714 if (isOverThresholdDrag) {
25715 // then dragging can happen
25716 var draggedEles = r.dragData.touchDragEles;
25717 var justStartedDrag = !r.dragData.didDrag;
25718
25719 if (justStartedDrag) {
25720 addNodesToDrag(draggedEles, {
25721 inDragLayer: true
25722 });
25723 }
25724
25725 r.dragData.didDrag = true;
25726 var totalShift = {
25727 x: 0,
25728 y: 0
25729 };
25730
25731 if (number(disp[0]) && number(disp[1])) {
25732 totalShift.x += disp[0];
25733 totalShift.y += disp[1];
25734
25735 if (justStartedDrag) {
25736 r.redrawHint('eles', true);
25737 var dragDelta = r.touchData.dragDelta;
25738
25739 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25740 totalShift.x += dragDelta[0];
25741 totalShift.y += dragDelta[1];
25742 }
25743 }
25744 }
25745
25746 r.hoverData.draggingEles = true;
25747 draggedEles.silentShift(totalShift).emit('position drag');
25748 r.redrawHint('drag', true);
25749
25750 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25751 r.redrawHint('eles', true);
25752 }
25753
25754 r.redraw();
25755 } else {
25756 // otherise keep track of drag delta for later
25757 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25758
25759 if (dragDelta.length === 0) {
25760 dragDelta.push(disp[0]);
25761 dragDelta.push(disp[1]);
25762 } else {
25763 dragDelta[0] += disp[0];
25764 dragDelta[1] += disp[1];
25765 }
25766 }
25767 } // touchmove
25768
25769
25770 {
25771 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25772 x: now[0],
25773 y: now[1]
25774 });
25775
25776 if ((!start || !start.grabbed()) && near != last) {
25777 if (last) {
25778 last.emit({
25779 originalEvent: e,
25780 type: 'tapdragout',
25781 position: {
25782 x: now[0],
25783 y: now[1]
25784 }
25785 });
25786 }
25787
25788 if (near) {
25789 near.emit({
25790 originalEvent: e,
25791 type: 'tapdragover',
25792 position: {
25793 x: now[0],
25794 y: now[1]
25795 }
25796 });
25797 }
25798 }
25799
25800 r.touchData.last = near;
25801 } // check to cancel taphold
25802
25803 if (capture) {
25804 for (var i = 0; i < now.length; i++) {
25805 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25806 r.touchData.singleTouchMoved = true;
25807 }
25808 }
25809 } // panning
25810
25811
25812 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25813 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25814
25815 if (allowPassthrough) {
25816 e.preventDefault();
25817
25818 if (!r.data.bgActivePosistion) {
25819 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25820 }
25821
25822 if (r.swipePanning) {
25823 cy.panBy({
25824 x: disp[0] * zoom,
25825 y: disp[1] * zoom
25826 });
25827 cy.emit('dragpan');
25828 } else if (isOverThresholdDrag) {
25829 r.swipePanning = true;
25830 cy.panBy({
25831 x: dx * zoom,
25832 y: dy * zoom
25833 });
25834 cy.emit('dragpan');
25835
25836 if (start) {
25837 start.unactivate();
25838 r.redrawHint('select', true);
25839 r.touchData.start = null;
25840 }
25841 }
25842 } // Re-project
25843
25844
25845 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25846 now[0] = pos[0];
25847 now[1] = pos[1];
25848 }
25849 }
25850
25851 for (var j = 0; j < now.length; j++) {
25852 earlier[j] = now[j];
25853 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25854
25855
25856 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
25857 r.data.bgActivePosistion = undefined;
25858 r.redrawHint('select', true);
25859 r.redraw();
25860 }
25861 }, false);
25862 var touchcancelHandler;
25863 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
25864 // eslint-disable-line no-unused-vars
25865 var start = r.touchData.start;
25866 r.touchData.capture = false;
25867
25868 if (start) {
25869 start.unactivate();
25870 }
25871 });
25872 var touchendHandler;
25873 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
25874 // eslint-disable-line no-unused-vars
25875 var start = r.touchData.start;
25876 var capture = r.touchData.capture;
25877
25878 if (capture) {
25879 if (e.touches.length === 0) {
25880 r.touchData.capture = false;
25881 }
25882
25883 e.preventDefault();
25884 } else {
25885 return;
25886 }
25887
25888 var select = r.selection;
25889 r.swipePanning = false;
25890 r.hoverData.draggingEles = false;
25891 var cy = r.cy;
25892 var zoom = cy.zoom();
25893 var now = r.touchData.now;
25894 var earlier = r.touchData.earlier;
25895
25896 if (e.touches[0]) {
25897 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25898 now[0] = pos[0];
25899 now[1] = pos[1];
25900 }
25901
25902 if (e.touches[1]) {
25903 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25904 now[2] = pos[0];
25905 now[3] = pos[1];
25906 }
25907
25908 if (e.touches[2]) {
25909 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25910 now[4] = pos[0];
25911 now[5] = pos[1];
25912 }
25913
25914 if (start) {
25915 start.unactivate();
25916 }
25917
25918 var ctxTapend;
25919
25920 if (r.touchData.cxt) {
25921 ctxTapend = {
25922 originalEvent: e,
25923 type: 'cxttapend',
25924 position: {
25925 x: now[0],
25926 y: now[1]
25927 }
25928 };
25929
25930 if (start) {
25931 start.emit(ctxTapend);
25932 } else {
25933 cy.emit(ctxTapend);
25934 }
25935
25936 if (!r.touchData.cxtDragged) {
25937 var ctxTap = {
25938 originalEvent: e,
25939 type: 'cxttap',
25940 position: {
25941 x: now[0],
25942 y: now[1]
25943 }
25944 };
25945
25946 if (start) {
25947 start.emit(ctxTap);
25948 } else {
25949 cy.emit(ctxTap);
25950 }
25951 }
25952
25953 if (r.touchData.start) {
25954 r.touchData.start._private.grabbed = false;
25955 }
25956
25957 r.touchData.cxt = false;
25958 r.touchData.start = null;
25959 r.redraw();
25960 return;
25961 } // no more box selection if we don't have three fingers
25962
25963
25964 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
25965 r.touchData.selecting = false;
25966 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25967 select[0] = undefined;
25968 select[1] = undefined;
25969 select[2] = undefined;
25970 select[3] = undefined;
25971 select[4] = 0;
25972 r.redrawHint('select', true);
25973 cy.emit({
25974 type: 'boxend',
25975 originalEvent: e,
25976 position: {
25977 x: now[0],
25978 y: now[1]
25979 }
25980 });
25981
25982 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25983 return ele.selectable() && !ele.selected();
25984 };
25985
25986 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25987
25988 if (box.nonempty()) {
25989 r.redrawHint('eles', true);
25990 }
25991
25992 r.redraw();
25993 }
25994
25995 if (start != null) {
25996 start.unactivate();
25997 }
25998
25999 if (e.touches[2]) {
26000 r.data.bgActivePosistion = undefined;
26001 r.redrawHint('select', true);
26002 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26003 r.data.bgActivePosistion = undefined;
26004 r.redrawHint('select', true);
26005 var draggedEles = r.dragData.touchDragEles;
26006
26007 if (start != null) {
26008 var startWasGrabbed = start._private.grabbed;
26009 freeDraggedElements(draggedEles);
26010 r.redrawHint('drag', true);
26011 r.redrawHint('eles', true);
26012
26013 if (startWasGrabbed) {
26014 start.emit('freeon');
26015 draggedEles.emit('free');
26016
26017 if (r.dragData.didDrag) {
26018 start.emit('dragfreeon');
26019 draggedEles.emit('dragfree');
26020 }
26021 }
26022
26023 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26024 x: now[0],
26025 y: now[1]
26026 });
26027 start.unactivate();
26028 r.touchData.start = null;
26029 } else {
26030 var near = r.findNearestElement(now[0], now[1], true, true);
26031 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26032 x: now[0],
26033 y: now[1]
26034 });
26035 }
26036
26037 var dx = r.touchData.startPosition[0] - now[0];
26038 var dx2 = dx * dx;
26039 var dy = r.touchData.startPosition[1] - now[1];
26040 var dy2 = dy * dy;
26041 var dist2 = dx2 + dy2;
26042 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26043
26044 if (!r.touchData.singleTouchMoved) {
26045 if (!start) {
26046 cy.$(':selected').unselect(['tapunselect']);
26047 }
26048
26049 triggerEvents(start, ['tap', 'vclick'], e, {
26050 x: now[0],
26051 y: now[1]
26052 });
26053 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26054
26055
26056 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26057 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26058 ) {
26059 if (cy.selectionType() === 'single') {
26060 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26061 start.select(['tapselect']);
26062 } else {
26063 if (start.selected()) {
26064 start.unselect(['tapunselect']);
26065 } else {
26066 start.select(['tapselect']);
26067 }
26068 }
26069
26070 r.redrawHint('eles', true);
26071 }
26072
26073 r.touchData.singleTouchMoved = true;
26074 }
26075
26076 for (var j = 0; j < now.length; j++) {
26077 earlier[j] = now[j];
26078 }
26079
26080 r.dragData.didDrag = false; // reset for next touchstart
26081
26082 if (e.touches.length === 0) {
26083 r.touchData.dragDelta = [];
26084 r.touchData.startPosition = null;
26085 r.touchData.startGPosition = null;
26086 r.touchData.didSelect = false;
26087 }
26088
26089 if (e.touches.length < 2) {
26090 if (e.touches.length === 1) {
26091 // the old start global pos'n may not be the same finger that remains
26092 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26093 }
26094
26095 r.pinching = false;
26096 r.redrawHint('eles', true);
26097 r.redraw();
26098 } //r.redraw();
26099
26100 }, false); // fallback compatibility layer for ms pointer events
26101
26102 if (typeof TouchEvent === 'undefined') {
26103 var pointers = [];
26104
26105 var makeTouch = function makeTouch(e) {
26106 return {
26107 clientX: e.clientX,
26108 clientY: e.clientY,
26109 force: 1,
26110 identifier: e.pointerId,
26111 pageX: e.pageX,
26112 pageY: e.pageY,
26113 radiusX: e.width / 2,
26114 radiusY: e.height / 2,
26115 screenX: e.screenX,
26116 screenY: e.screenY,
26117 target: e.target
26118 };
26119 };
26120
26121 var makePointer = function makePointer(e) {
26122 return {
26123 event: e,
26124 touch: makeTouch(e)
26125 };
26126 };
26127
26128 var addPointer = function addPointer(e) {
26129 pointers.push(makePointer(e));
26130 };
26131
26132 var removePointer = function removePointer(e) {
26133 for (var i = 0; i < pointers.length; i++) {
26134 var p = pointers[i];
26135
26136 if (p.event.pointerId === e.pointerId) {
26137 pointers.splice(i, 1);
26138 return;
26139 }
26140 }
26141 };
26142
26143 var updatePointer = function updatePointer(e) {
26144 var p = pointers.filter(function (p) {
26145 return p.event.pointerId === e.pointerId;
26146 })[0];
26147 p.event = e;
26148 p.touch = makeTouch(e);
26149 };
26150
26151 var addTouchesToEvent = function addTouchesToEvent(e) {
26152 e.touches = pointers.map(function (p) {
26153 return p.touch;
26154 });
26155 };
26156
26157 var pointerIsMouse = function pointerIsMouse(e) {
26158 return e.pointerType === 'mouse' || e.pointerType === 4;
26159 };
26160
26161 r.registerBinding(r.container, 'pointerdown', function (e) {
26162 if (pointerIsMouse(e)) {
26163 return;
26164 } // mouse already handled
26165
26166
26167 e.preventDefault();
26168 addPointer(e);
26169 addTouchesToEvent(e);
26170 touchstartHandler(e);
26171 });
26172 r.registerBinding(r.container, 'pointerup', function (e) {
26173 if (pointerIsMouse(e)) {
26174 return;
26175 } // mouse already handled
26176
26177
26178 removePointer(e);
26179 addTouchesToEvent(e);
26180 touchendHandler(e);
26181 });
26182 r.registerBinding(r.container, 'pointercancel', function (e) {
26183 if (pointerIsMouse(e)) {
26184 return;
26185 } // mouse already handled
26186
26187
26188 removePointer(e);
26189 addTouchesToEvent(e);
26190 touchcancelHandler(e);
26191 });
26192 r.registerBinding(r.container, 'pointermove', function (e) {
26193 if (pointerIsMouse(e)) {
26194 return;
26195 } // mouse already handled
26196
26197
26198 e.preventDefault();
26199 updatePointer(e);
26200 addTouchesToEvent(e);
26201 touchmoveHandler(e);
26202 });
26203 }
26204};
26205
26206var BRp$d = {};
26207
26208BRp$d.generatePolygon = function (name, points) {
26209 return this.nodeShapes[name] = {
26210 renderer: this,
26211 name: name,
26212 points: points,
26213 draw: function draw(context, centerX, centerY, width, height) {
26214 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26215 },
26216 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26217 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26218 },
26219 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26220 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26221 }
26222 };
26223};
26224
26225BRp$d.generateEllipse = function () {
26226 return this.nodeShapes['ellipse'] = {
26227 renderer: this,
26228 name: 'ellipse',
26229 draw: function draw(context, centerX, centerY, width, height) {
26230 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26231 },
26232 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26233 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26234 },
26235 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26236 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26237 }
26238 };
26239};
26240
26241BRp$d.generateRoundPolygon = function (name, points) {
26242 // Pre-compute control points
26243 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26244 // the unit vectors.
26245 // For simplicity the layout will be:
26246 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26247 var allPoints = new Array(points.length * 2);
26248
26249 for (var i = 0; i < points.length / 2; i++) {
26250 var sourceIndex = i * 2;
26251 var destIndex = void 0;
26252
26253 if (i < points.length / 2 - 1) {
26254 destIndex = (i + 1) * 2;
26255 } else {
26256 destIndex = 0;
26257 }
26258
26259 allPoints[i * 4] = points[sourceIndex];
26260 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26261 var xDest = points[destIndex] - points[sourceIndex];
26262 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26263 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26264 allPoints[i * 4 + 2] = xDest / norm;
26265 allPoints[i * 4 + 3] = yDest / norm;
26266 }
26267
26268 return this.nodeShapes[name] = {
26269 renderer: this,
26270 name: name,
26271 points: allPoints,
26272 draw: function draw(context, centerX, centerY, width, height) {
26273 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26274 },
26275 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26276 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26277 },
26278 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26279 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26280 }
26281 };
26282};
26283
26284BRp$d.generateRoundRectangle = function () {
26285 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26286 renderer: this,
26287 name: 'round-rectangle',
26288 points: generateUnitNgonPointsFitToSquare(4, 0),
26289 draw: function draw(context, centerX, centerY, width, height) {
26290 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26291 },
26292 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26293 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26294 },
26295 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26296 var cornerRadius = getRoundRectangleRadius(width, height);
26297 var diam = cornerRadius * 2; // Check hBox
26298
26299 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26300 return true;
26301 } // Check vBox
26302
26303
26304 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26305 return true;
26306 } // Check top left quarter circle
26307
26308
26309 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26310 return true;
26311 } // Check top right quarter circle
26312
26313
26314 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26315 return true;
26316 } // Check bottom right quarter circle
26317
26318
26319 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26320 return true;
26321 } // Check bottom left quarter circle
26322
26323
26324 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26325 return true;
26326 }
26327
26328 return false;
26329 }
26330 };
26331};
26332
26333BRp$d.generateCutRectangle = function () {
26334 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26335 renderer: this,
26336 name: 'cut-rectangle',
26337 cornerLength: getCutRectangleCornerLength(),
26338 points: generateUnitNgonPointsFitToSquare(4, 0),
26339 draw: function draw(context, centerX, centerY, width, height) {
26340 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26341 },
26342 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
26343 var cl = this.cornerLength;
26344 var hh = height / 2;
26345 var hw = width / 2;
26346 var xBegin = centerX - hw;
26347 var xEnd = centerX + hw;
26348 var yBegin = centerY - hh;
26349 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26350
26351 return {
26352 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26353 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26354 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26355 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26356 };
26357 },
26358 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26359 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26360 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26361 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26362 },
26363 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26364 // Check hBox
26365 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
26366 return true;
26367 } // Check vBox
26368
26369
26370 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
26371 return true;
26372 }
26373
26374 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26375 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26376 }
26377 };
26378};
26379
26380BRp$d.generateBarrel = function () {
26381 return this.nodeShapes['barrel'] = {
26382 renderer: this,
26383 name: 'barrel',
26384 points: generateUnitNgonPointsFitToSquare(4, 0),
26385 draw: function draw(context, centerX, centerY, width, height) {
26386 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26387 },
26388 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26389 // use two fixed t values for the bezier curve approximation
26390 var t0 = 0.15;
26391 var t1 = 0.5;
26392 var t2 = 0.85;
26393 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26394
26395 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26396 // approximate curve pts based on the two t values
26397 var m0 = qbezierPtAt({
26398 x: pts[0],
26399 y: pts[1]
26400 }, {
26401 x: pts[2],
26402 y: pts[3]
26403 }, {
26404 x: pts[4],
26405 y: pts[5]
26406 }, t0);
26407 var m1 = qbezierPtAt({
26408 x: pts[0],
26409 y: pts[1]
26410 }, {
26411 x: pts[2],
26412 y: pts[3]
26413 }, {
26414 x: pts[4],
26415 y: pts[5]
26416 }, t1);
26417 var m2 = qbezierPtAt({
26418 x: pts[0],
26419 y: pts[1]
26420 }, {
26421 x: pts[2],
26422 y: pts[3]
26423 }, {
26424 x: pts[4],
26425 y: pts[5]
26426 }, t2);
26427 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26428 };
26429
26430 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26431 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26432 },
26433 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26434 var hh = height / 2;
26435 var hw = width / 2;
26436 var xBegin = centerX - hw;
26437 var xEnd = centerX + hw;
26438 var yBegin = centerY - hh;
26439 var yEnd = centerY + hh;
26440 var curveConstants = getBarrelCurveConstants(width, height);
26441 var hOffset = curveConstants.heightOffset;
26442 var wOffset = curveConstants.widthOffset;
26443 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26444
26445 var pts = {
26446 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26447 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26448 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26449 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26450 };
26451 pts.topLeft.isTop = true;
26452 pts.topRight.isTop = true;
26453 pts.bottomLeft.isBottom = true;
26454 pts.bottomRight.isBottom = true;
26455 return pts;
26456 },
26457 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26458 var curveConstants = getBarrelCurveConstants(width, height);
26459 var hOffset = curveConstants.heightOffset;
26460 var wOffset = curveConstants.widthOffset; // Check hBox
26461
26462 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26463 return true;
26464 } // Check vBox
26465
26466
26467 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26468 return true;
26469 }
26470
26471 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26472
26473 var getCurveT = function getCurveT(x, y, curvePts) {
26474 var x0 = curvePts[4];
26475 var x1 = curvePts[2];
26476 var x2 = curvePts[0];
26477 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26478
26479 var y2 = curvePts[1];
26480 var xMin = Math.min(x0, x2);
26481 var xMax = Math.max(x0, x2);
26482 var yMin = Math.min(y0, y2);
26483 var yMax = Math.max(y0, y2);
26484
26485 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26486 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26487 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26488 var validRoots = roots.filter(function (r) {
26489 return 0 <= r && r <= 1;
26490 });
26491
26492 if (validRoots.length > 0) {
26493 return validRoots[0];
26494 }
26495 }
26496
26497 return null;
26498 };
26499
26500 var curveRegions = Object.keys(barrelCurvePts);
26501
26502 for (var i = 0; i < curveRegions.length; i++) {
26503 var corner = curveRegions[i];
26504 var cornerPts = barrelCurvePts[corner];
26505 var t = getCurveT(x, y, cornerPts);
26506
26507 if (t == null) {
26508 continue;
26509 }
26510
26511 var y0 = cornerPts[5];
26512 var y1 = cornerPts[3];
26513 var y2 = cornerPts[1];
26514 var bezY = qbezierAt(y0, y1, y2, t);
26515
26516 if (cornerPts.isTop && bezY <= y) {
26517 return true;
26518 }
26519
26520 if (cornerPts.isBottom && y <= bezY) {
26521 return true;
26522 }
26523 }
26524
26525 return false;
26526 }
26527 };
26528};
26529
26530BRp$d.generateBottomRoundrectangle = function () {
26531 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26532 renderer: this,
26533 name: 'bottom-round-rectangle',
26534 points: generateUnitNgonPointsFitToSquare(4, 0),
26535 draw: function draw(context, centerX, centerY, width, height) {
26536 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26537 },
26538 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26539 var topStartX = nodeX - (width / 2 + padding);
26540 var topStartY = nodeY - (height / 2 + padding);
26541 var topEndY = topStartY;
26542 var topEndX = nodeX + (width / 2 + padding);
26543 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26544
26545 if (topIntersections.length > 0) {
26546 return topIntersections;
26547 }
26548
26549 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26550 },
26551 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26552 var cornerRadius = getRoundRectangleRadius(width, height);
26553 var diam = 2 * cornerRadius; // Check hBox
26554
26555 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26556 return true;
26557 } // Check vBox
26558
26559
26560 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26561 return true;
26562 } // check non-rounded top side
26563
26564
26565 var outerWidth = width / 2 + 2 * padding;
26566 var outerHeight = height / 2 + 2 * padding;
26567 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26568
26569 if (pointInsidePolygonPoints(x, y, points)) {
26570 return true;
26571 } // Check bottom right quarter circle
26572
26573
26574 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26575 return true;
26576 } // Check bottom left quarter circle
26577
26578
26579 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26580 return true;
26581 }
26582
26583 return false;
26584 }
26585 };
26586};
26587
26588BRp$d.registerNodeShapes = function () {
26589 var nodeShapes = this.nodeShapes = {};
26590 var renderer = this;
26591 this.generateEllipse();
26592 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26593 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26594 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26595 nodeShapes['square'] = nodeShapes['rectangle'];
26596 this.generateRoundRectangle();
26597 this.generateCutRectangle();
26598 this.generateBarrel();
26599 this.generateBottomRoundrectangle();
26600 {
26601 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26602 this.generatePolygon('diamond', diamondPoints);
26603 this.generateRoundPolygon('round-diamond', diamondPoints);
26604 }
26605 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26606 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26607 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26608 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26609 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26610 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26611 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26612 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26613 var star5Points = new Array(20);
26614 {
26615 var outerPoints = generateUnitNgonPoints(5, 0);
26616 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26617
26618 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26619 innerRadius *= 1.57;
26620
26621 for (var i = 0; i < innerPoints.length / 2; i++) {
26622 innerPoints[i * 2] *= innerRadius;
26623 innerPoints[i * 2 + 1] *= innerRadius;
26624 }
26625
26626 for (var i = 0; i < 20 / 4; i++) {
26627 star5Points[i * 4] = outerPoints[i * 2];
26628 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26629 star5Points[i * 4 + 2] = innerPoints[i * 2];
26630 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26631 }
26632 }
26633 star5Points = fitPolygonToSquare(star5Points);
26634 this.generatePolygon('star', star5Points);
26635 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26636 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26637 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]);
26638 {
26639 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26640 this.generatePolygon('tag', tagPoints);
26641 this.generateRoundPolygon('round-tag', tagPoints);
26642 }
26643
26644 nodeShapes.makePolygon = function (points) {
26645 // use caching on user-specified polygons so they are as fast as native shapes
26646 var key = points.join('$');
26647 var name = 'polygon-' + key;
26648 var shape;
26649
26650 if (shape = this[name]) {
26651 // got cached shape
26652 return shape;
26653 } // create and cache new shape
26654
26655
26656 return renderer.generatePolygon(name, points);
26657 };
26658};
26659
26660var BRp$e = {};
26661
26662BRp$e.timeToRender = function () {
26663 return this.redrawTotalTime / this.redrawCount;
26664};
26665
26666BRp$e.redraw = function (options) {
26667 options = options || staticEmptyObject();
26668 var r = this;
26669
26670 if (r.averageRedrawTime === undefined) {
26671 r.averageRedrawTime = 0;
26672 }
26673
26674 if (r.lastRedrawTime === undefined) {
26675 r.lastRedrawTime = 0;
26676 }
26677
26678 if (r.lastDrawTime === undefined) {
26679 r.lastDrawTime = 0;
26680 }
26681
26682 r.requestedFrame = true;
26683 r.renderOptions = options;
26684};
26685
26686BRp$e.beforeRender = function (fn, priority) {
26687 // the renderer can't add tick callbacks when destroyed
26688 if (this.destroyed) {
26689 return;
26690 }
26691
26692 if (priority == null) {
26693 error('Priority is not optional for beforeRender');
26694 }
26695
26696 var cbs = this.beforeRenderCallbacks;
26697 cbs.push({
26698 fn: fn,
26699 priority: priority
26700 }); // higher priority callbacks executed first
26701
26702 cbs.sort(function (a, b) {
26703 return b.priority - a.priority;
26704 });
26705};
26706
26707var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26708 var cbs = r.beforeRenderCallbacks;
26709
26710 for (var i = 0; i < cbs.length; i++) {
26711 cbs[i].fn(willDraw, startTime);
26712 }
26713};
26714
26715BRp$e.startRenderLoop = function () {
26716 var r = this;
26717 var cy = r.cy;
26718
26719 if (r.renderLoopStarted) {
26720 return;
26721 } else {
26722 r.renderLoopStarted = true;
26723 }
26724
26725 var renderFn = function renderFn(requestTime) {
26726 if (r.destroyed) {
26727 return;
26728 }
26729
26730 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26731 beforeRenderCallbacks(r, true, requestTime);
26732 var startTime = performanceNow();
26733 r.render(r.renderOptions);
26734 var endTime = r.lastDrawTime = performanceNow();
26735
26736 if (r.averageRedrawTime === undefined) {
26737 r.averageRedrawTime = endTime - startTime;
26738 }
26739
26740 if (r.redrawCount === undefined) {
26741 r.redrawCount = 0;
26742 }
26743
26744 r.redrawCount++;
26745
26746 if (r.redrawTotalTime === undefined) {
26747 r.redrawTotalTime = 0;
26748 }
26749
26750 var duration = endTime - startTime;
26751 r.redrawTotalTime += duration;
26752 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26753
26754 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26755 r.requestedFrame = false;
26756 } else {
26757 beforeRenderCallbacks(r, false, requestTime);
26758 }
26759
26760 r.skipFrame = false;
26761 requestAnimationFrame(renderFn);
26762 };
26763
26764 requestAnimationFrame(renderFn);
26765};
26766
26767var BaseRenderer = function BaseRenderer(options) {
26768 this.init(options);
26769};
26770
26771var BR = BaseRenderer;
26772var BRp$f = BR.prototype;
26773BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26774
26775BRp$f.init = function (options) {
26776 var r = this;
26777 r.options = options;
26778 r.cy = options.cy;
26779 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26780
26781 if (window$1) {
26782 var document = window$1.document;
26783 var head = document.head;
26784 var stylesheetId = '__________cytoscape_stylesheet';
26785 var className = '__________cytoscape_container';
26786 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26787
26788 if (ctr.className.indexOf(className) < 0) {
26789 ctr.className = (ctr.className || '') + ' ' + className;
26790 }
26791
26792 if (!stylesheetAlreadyExists) {
26793 var stylesheet = document.createElement('style');
26794 stylesheet.id = stylesheetId;
26795 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26796 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26797 }
26798
26799 var computedStyle = window$1.getComputedStyle(ctr);
26800 var position = computedStyle.getPropertyValue('position');
26801
26802 if (position === 'static') {
26803 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26804 }
26805 }
26806
26807 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26808
26809 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
26810
26811 r.hoverData = {
26812 down: null,
26813 last: null,
26814 downTime: null,
26815 triggerMode: null,
26816 dragging: false,
26817 initialPan: [null, null],
26818 capture: false
26819 };
26820 r.dragData = {
26821 possibleDragElements: []
26822 };
26823 r.touchData = {
26824 start: null,
26825 capture: false,
26826 // These 3 fields related to tap, taphold events
26827 startPosition: [null, null, null, null, null, null],
26828 singleTouchStartTime: null,
26829 singleTouchMoved: true,
26830 now: [null, null, null, null, null, null],
26831 earlier: [null, null, null, null, null, null]
26832 };
26833 r.redraws = 0;
26834 r.showFps = options.showFps;
26835 r.debug = options.debug;
26836 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
26837 r.textureOnViewport = options.textureOnViewport;
26838 r.wheelSensitivity = options.wheelSensitivity;
26839 r.motionBlurEnabled = options.motionBlur; // on by default
26840
26841 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
26842 r.motionBlur = options.motionBlur; // for initial kick off
26843
26844 r.motionBlurOpacity = options.motionBlurOpacity;
26845 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
26846 r.motionBlurPxRatio = 1;
26847 r.mbPxRBlurry = 1; //0.8;
26848
26849 r.minMbLowQualFrames = 4;
26850 r.fullQualityMb = false;
26851 r.clearedForMotionBlur = [];
26852 r.desktopTapThreshold = options.desktopTapThreshold;
26853 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
26854 r.touchTapThreshold = options.touchTapThreshold;
26855 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
26856 r.tapholdDuration = 500;
26857 r.bindings = [];
26858 r.beforeRenderCallbacks = [];
26859 r.beforeRenderPriorities = {
26860 // higher priority execs before lower one
26861 animations: 400,
26862 eleCalcs: 300,
26863 eleTxrDeq: 200,
26864 lyrTxrDeq: 150,
26865 lyrTxrSkip: 100
26866 };
26867 r.registerNodeShapes();
26868 r.registerArrowShapes();
26869 r.registerCalculationListeners();
26870};
26871
26872BRp$f.notify = function (eventName, eles) {
26873 var r = this;
26874 var cy = r.cy; // the renderer can't be notified after it's destroyed
26875
26876 if (this.destroyed) {
26877 return;
26878 }
26879
26880 if (eventName === 'init') {
26881 r.load();
26882 return;
26883 }
26884
26885 if (eventName === 'destroy') {
26886 r.destroy();
26887 return;
26888 }
26889
26890 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
26891 r.invalidateCachedZSortedEles();
26892 }
26893
26894 if (eventName === 'viewport') {
26895 r.redrawHint('select', true);
26896 }
26897
26898 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
26899 r.invalidateContainerClientCoordsCache();
26900 r.matchCanvasSize(r.container);
26901 }
26902
26903 r.redrawHint('eles', true);
26904 r.redrawHint('drag', true);
26905 this.startRenderLoop();
26906 this.redraw();
26907};
26908
26909BRp$f.destroy = function () {
26910 var r = this;
26911 r.destroyed = true;
26912 r.cy.stopAnimationLoop();
26913
26914 for (var i = 0; i < r.bindings.length; i++) {
26915 var binding = r.bindings[i];
26916 var b = binding;
26917 var tgt = b.target;
26918 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
26919 }
26920
26921 r.bindings = [];
26922 r.beforeRenderCallbacks = [];
26923 r.onUpdateEleCalcsFns = [];
26924
26925 if (r.removeObserver) {
26926 r.removeObserver.disconnect();
26927 }
26928
26929 if (r.styleObserver) {
26930 r.styleObserver.disconnect();
26931 }
26932
26933 if (r.resizeObserver) {
26934 r.resizeObserver.disconnect();
26935 }
26936
26937 if (r.labelCalcDiv) {
26938 try {
26939 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
26940 } catch (e) {// ie10 issue #1014
26941 }
26942 }
26943};
26944
26945BRp$f.isHeadless = function () {
26946 return false;
26947};
26948
26949[BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
26950 extend(BRp$f, props);
26951});
26952
26953var fullFpsTime = 1000 / 60; // assume 60 frames per second
26954
26955var defs = {
26956 setupDequeueing: function setupDequeueing(opts) {
26957 return function setupDequeueingImpl() {
26958 var self = this;
26959 var r = this.renderer;
26960
26961 if (self.dequeueingSetup) {
26962 return;
26963 } else {
26964 self.dequeueingSetup = true;
26965 }
26966
26967 var queueRedraw = util(function () {
26968 r.redrawHint('eles', true);
26969 r.redrawHint('drag', true);
26970 r.redraw();
26971 }, opts.deqRedrawThreshold);
26972
26973 var dequeue = function dequeue(willDraw, frameStartTime) {
26974 var startTime = performanceNow();
26975 var avgRenderTime = r.averageRedrawTime;
26976 var renderTime = r.lastRedrawTime;
26977 var deqd = [];
26978 var extent = r.cy.extent();
26979 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
26980 // queue won't automatically be flushed before dequeueing starts
26981
26982 if (!willDraw) {
26983 r.flushRenderedStyleQueue();
26984 }
26985
26986 while (true) {
26987 // eslint-disable-line no-constant-condition
26988 var now = performanceNow();
26989 var duration = now - startTime;
26990 var frameDuration = now - frameStartTime;
26991
26992 if (renderTime < fullFpsTime) {
26993 // if we're rendering faster than the ideal fps, then do dequeueing
26994 // during all of the remaining frame time
26995 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
26996
26997 if (frameDuration >= opts.deqFastCost * timeAvailable) {
26998 break;
26999 }
27000 } else {
27001 if (willDraw) {
27002 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
27003 break;
27004 }
27005 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
27006 break;
27007 }
27008 }
27009
27010 var thisDeqd = opts.deq(self, pixelRatio, extent);
27011
27012 if (thisDeqd.length > 0) {
27013 for (var i = 0; i < thisDeqd.length; i++) {
27014 deqd.push(thisDeqd[i]);
27015 }
27016 } else {
27017 break;
27018 }
27019 } // callbacks on dequeue
27020
27021
27022 if (deqd.length > 0) {
27023 opts.onDeqd(self, deqd);
27024
27025 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27026 queueRedraw();
27027 }
27028 }
27029 };
27030
27031 var priority = opts.priority || noop;
27032 r.beforeRender(dequeue, priority(self));
27033 };
27034 }
27035};
27036
27037// Uses keys so elements may share the same cache.
27038
27039var ElementTextureCacheLookup =
27040/*#__PURE__*/
27041function () {
27042 function ElementTextureCacheLookup(getKey) {
27043 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27044
27045 _classCallCheck(this, ElementTextureCacheLookup);
27046
27047 this.idsByKey = new Map$1();
27048 this.keyForId = new Map$1();
27049 this.cachesByLvl = new Map$1();
27050 this.lvls = [];
27051 this.getKey = getKey;
27052 this.doesEleInvalidateKey = doesEleInvalidateKey;
27053 }
27054
27055 _createClass(ElementTextureCacheLookup, [{
27056 key: "getIdsFor",
27057 value: function getIdsFor(key) {
27058 if (key == null) {
27059 error("Can not get id list for null key");
27060 }
27061
27062 var idsByKey = this.idsByKey;
27063 var ids = this.idsByKey.get(key);
27064
27065 if (!ids) {
27066 ids = new Set$1();
27067 idsByKey.set(key, ids);
27068 }
27069
27070 return ids;
27071 }
27072 }, {
27073 key: "addIdForKey",
27074 value: function addIdForKey(key, id) {
27075 if (key != null) {
27076 this.getIdsFor(key).add(id);
27077 }
27078 }
27079 }, {
27080 key: "deleteIdForKey",
27081 value: function deleteIdForKey(key, id) {
27082 if (key != null) {
27083 this.getIdsFor(key)["delete"](id);
27084 }
27085 }
27086 }, {
27087 key: "getNumberOfIdsForKey",
27088 value: function getNumberOfIdsForKey(key) {
27089 if (key == null) {
27090 return 0;
27091 } else {
27092 return this.getIdsFor(key).size;
27093 }
27094 }
27095 }, {
27096 key: "updateKeyMappingFor",
27097 value: function updateKeyMappingFor(ele) {
27098 var id = ele.id();
27099 var prevKey = this.keyForId.get(id);
27100 var currKey = this.getKey(ele);
27101 this.deleteIdForKey(prevKey, id);
27102 this.addIdForKey(currKey, id);
27103 this.keyForId.set(id, currKey);
27104 }
27105 }, {
27106 key: "deleteKeyMappingFor",
27107 value: function deleteKeyMappingFor(ele) {
27108 var id = ele.id();
27109 var prevKey = this.keyForId.get(id);
27110 this.deleteIdForKey(prevKey, id);
27111 this.keyForId["delete"](id);
27112 }
27113 }, {
27114 key: "keyHasChangedFor",
27115 value: function keyHasChangedFor(ele) {
27116 var id = ele.id();
27117 var prevKey = this.keyForId.get(id);
27118 var newKey = this.getKey(ele);
27119 return prevKey !== newKey;
27120 }
27121 }, {
27122 key: "isInvalid",
27123 value: function isInvalid(ele) {
27124 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27125 }
27126 }, {
27127 key: "getCachesAt",
27128 value: function getCachesAt(lvl) {
27129 var cachesByLvl = this.cachesByLvl,
27130 lvls = this.lvls;
27131 var caches = cachesByLvl.get(lvl);
27132
27133 if (!caches) {
27134 caches = new Map$1();
27135 cachesByLvl.set(lvl, caches);
27136 lvls.push(lvl);
27137 }
27138
27139 return caches;
27140 }
27141 }, {
27142 key: "getCache",
27143 value: function getCache(key, lvl) {
27144 return this.getCachesAt(lvl).get(key);
27145 }
27146 }, {
27147 key: "get",
27148 value: function get(ele, lvl) {
27149 var key = this.getKey(ele);
27150 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27151
27152 if (cache != null) {
27153 this.updateKeyMappingFor(ele);
27154 }
27155
27156 return cache;
27157 }
27158 }, {
27159 key: "getForCachedKey",
27160 value: function getForCachedKey(ele, lvl) {
27161 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27162
27163 var cache = this.getCache(key, lvl);
27164 return cache;
27165 }
27166 }, {
27167 key: "hasCache",
27168 value: function hasCache(key, lvl) {
27169 return this.getCachesAt(lvl).has(key);
27170 }
27171 }, {
27172 key: "has",
27173 value: function has(ele, lvl) {
27174 var key = this.getKey(ele);
27175 return this.hasCache(key, lvl);
27176 }
27177 }, {
27178 key: "setCache",
27179 value: function setCache(key, lvl, cache) {
27180 cache.key = key;
27181 this.getCachesAt(lvl).set(key, cache);
27182 }
27183 }, {
27184 key: "set",
27185 value: function set(ele, lvl, cache) {
27186 var key = this.getKey(ele);
27187 this.setCache(key, lvl, cache);
27188 this.updateKeyMappingFor(ele);
27189 }
27190 }, {
27191 key: "deleteCache",
27192 value: function deleteCache(key, lvl) {
27193 this.getCachesAt(lvl)["delete"](key);
27194 }
27195 }, {
27196 key: "delete",
27197 value: function _delete(ele, lvl) {
27198 var key = this.getKey(ele);
27199 this.deleteCache(key, lvl);
27200 }
27201 }, {
27202 key: "invalidateKey",
27203 value: function invalidateKey(key) {
27204 var _this = this;
27205
27206 this.lvls.forEach(function (lvl) {
27207 return _this.deleteCache(key, lvl);
27208 });
27209 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27210
27211 }, {
27212 key: "invalidate",
27213 value: function invalidate(ele) {
27214 var id = ele.id();
27215 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27216
27217 this.deleteKeyMappingFor(ele);
27218 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27219
27220 if (entireKeyInvalidated) {
27221 // clear mapping for current key
27222 this.invalidateKey(key);
27223 }
27224
27225 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27226 }
27227 }]);
27228
27229 return ElementTextureCacheLookup;
27230}();
27231
27232var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27233
27234var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27235
27236var minLvl = -4; // when scaling smaller than that we don't need to re-render
27237
27238var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27239
27240var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27241
27242var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27243
27244var defTxrWidth = 1024; // default/minimum texture width
27245
27246var maxTxrW = 1024; // the maximum width of a texture
27247
27248var maxTxrH = 1024; // the maximum height of a texture
27249
27250var minUtility = 0.2; // if usage of texture is less than this, it is retired
27251
27252var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27253
27254var maxFullnessChecks = 10; // dequeued after this many checks
27255
27256var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27257
27258var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27259
27260var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27261
27262var deqFastCost = 0.9; // % of frame time to be used when >60fps
27263
27264var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27265
27266var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27267
27268var getTxrReasons = {
27269 dequeue: 'dequeue',
27270 downscale: 'downscale',
27271 highQuality: 'highQuality'
27272};
27273var initDefaults = defaults({
27274 getKey: null,
27275 doesEleInvalidateKey: falsify,
27276 drawElement: null,
27277 getBoundingBox: null,
27278 getRotationPoint: null,
27279 getRotationOffset: null,
27280 isVisible: trueify,
27281 allowEdgeTxrCaching: true,
27282 allowParentTxrCaching: true
27283});
27284
27285var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27286 var self = this;
27287 self.renderer = renderer;
27288 self.onDequeues = [];
27289 var opts = initDefaults(initOptions);
27290 extend(self, opts);
27291 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27292 self.setupDequeueing();
27293};
27294
27295var ETCp = ElementTextureCache.prototype;
27296ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27297
27298ETCp.getTextureQueue = function (txrH) {
27299 var self = this;
27300 self.eleImgCaches = self.eleImgCaches || {};
27301 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27302}; // the list of usused textures which can be recycled (in use in texture queue)
27303
27304
27305ETCp.getRetiredTextureQueue = function (txrH) {
27306 var self = this;
27307 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27308 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27309 return rtxtrQ;
27310}; // queue of element draw requests at different scale levels
27311
27312
27313ETCp.getElementQueue = function () {
27314 var self = this;
27315 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
27316 return b.reqs - a.reqs;
27317 });
27318 return q;
27319}; // queue of element draw requests at different scale levels (element id lookup)
27320
27321
27322ETCp.getElementKeyToQueue = function () {
27323 var self = this;
27324 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27325 return k2q;
27326};
27327
27328ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27329 var self = this;
27330 var r = this.renderer;
27331 var zoom = r.cy.zoom();
27332 var lookup = this.lookup;
27333
27334 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
27335 return null;
27336 }
27337
27338 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27339 return null;
27340 }
27341
27342 if (lvl == null) {
27343 lvl = Math.ceil(log2(zoom * pxRatio));
27344 }
27345
27346 if (lvl < minLvl) {
27347 lvl = minLvl;
27348 } else if (zoom >= maxZoom || lvl > maxLvl) {
27349 return null;
27350 }
27351
27352 var scale = Math.pow(2, lvl);
27353 var eleScaledH = bb.h * scale;
27354 var eleScaledW = bb.w * scale;
27355 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27356
27357 if (!this.isVisible(ele, scaledLabelShown)) {
27358 return null;
27359 }
27360
27361 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
27362
27363 if (eleCache && eleCache.invalidated) {
27364 eleCache.invalidated = false;
27365 eleCache.texture.invalidatedWidth -= eleCache.width;
27366 }
27367
27368 if (eleCache) {
27369 return eleCache;
27370 }
27371
27372 var txrH; // which texture height this ele belongs to
27373
27374 if (eleScaledH <= minTxrH) {
27375 txrH = minTxrH;
27376 } else if (eleScaledH <= txrStepH) {
27377 txrH = txrStepH;
27378 } else {
27379 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27380 }
27381
27382 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27383 return null; // caching large elements is not efficient
27384 }
27385
27386 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
27387
27388 var txr = txrQ[txrQ.length - 2];
27389
27390 var addNewTxr = function addNewTxr() {
27391 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27392 }; // try the last one if there is no second last one
27393
27394
27395 if (!txr) {
27396 txr = txrQ[txrQ.length - 1];
27397 } // if the last one doesn't exist, we need a first one
27398
27399
27400 if (!txr) {
27401 txr = addNewTxr();
27402 } // if there's no room in the current texture, we need a new one
27403
27404
27405 if (txr.width - txr.usedWidth < eleScaledW) {
27406 txr = addNewTxr();
27407 }
27408
27409 var scalableFrom = function scalableFrom(otherCache) {
27410 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27411 };
27412
27413 var deqing = reason && reason === getTxrReasons.dequeue;
27414 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27415 var downscaleReq = reason && reason === getTxrReasons.downscale;
27416 var higherCache; // the nearest cache with a higher level
27417
27418 for (var l = lvl + 1; l <= maxLvl; l++) {
27419 var c = lookup.get(ele, l);
27420
27421 if (c) {
27422 higherCache = c;
27423 break;
27424 }
27425 }
27426
27427 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27428
27429 var downscale = function downscale() {
27430 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27431 }; // reset ele area in texture
27432
27433
27434 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27435 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27436
27437 if (scalableFrom(oneUpCache)) {
27438 // then we can relatively cheaply rescale the existing image w/o rerendering
27439 downscale();
27440 } else if (scalableFrom(higherCache)) {
27441 // then use the higher cache for now and queue the next level down
27442 // to cheaply scale towards the smaller level
27443 if (highQualityReq) {
27444 for (var _l = higherCache.level; _l > lvl; _l--) {
27445 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27446 }
27447
27448 downscale();
27449 } else {
27450 self.queueElement(ele, higherCache.level - 1);
27451 return higherCache;
27452 }
27453 } else {
27454 var lowerCache; // the nearest cache with a lower level
27455
27456 if (!deqing && !highQualityReq && !downscaleReq) {
27457 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
27458 var _c = lookup.get(ele, _l2);
27459
27460 if (_c) {
27461 lowerCache = _c;
27462 break;
27463 }
27464 }
27465 }
27466
27467 if (scalableFrom(lowerCache)) {
27468 // then use the lower quality cache for now and queue the better one for later
27469 self.queueElement(ele, lvl);
27470 return lowerCache;
27471 }
27472
27473 txr.context.translate(txr.usedWidth, 0);
27474 txr.context.scale(scale, scale);
27475 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27476 txr.context.scale(1 / scale, 1 / scale);
27477 txr.context.translate(-txr.usedWidth, 0);
27478 }
27479
27480 eleCache = {
27481 x: txr.usedWidth,
27482 texture: txr,
27483 level: lvl,
27484 scale: scale,
27485 width: eleScaledW,
27486 height: eleScaledH,
27487 scaledLabelShown: scaledLabelShown
27488 };
27489 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27490 txr.eleCaches.push(eleCache);
27491 lookup.set(ele, lvl, eleCache);
27492 self.checkTextureFullness(txr);
27493 return eleCache;
27494};
27495
27496ETCp.invalidateElements = function (eles) {
27497 for (var i = 0; i < eles.length; i++) {
27498 this.invalidateElement(eles[i]);
27499 }
27500};
27501
27502ETCp.invalidateElement = function (ele) {
27503 var self = this;
27504 var lookup = self.lookup;
27505 var caches = [];
27506 var invalid = lookup.isInvalid(ele);
27507
27508 if (!invalid) {
27509 return; // override the invalidation request if the element key has not changed
27510 }
27511
27512 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
27513 var cache = lookup.getForCachedKey(ele, lvl);
27514
27515 if (cache) {
27516 caches.push(cache);
27517 }
27518 }
27519
27520 var noOtherElesUseCache = lookup.invalidate(ele);
27521
27522 if (noOtherElesUseCache) {
27523 for (var i = 0; i < caches.length; i++) {
27524 var _cache = caches[i];
27525 var txr = _cache.texture; // remove space from the texture it belongs to
27526
27527 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27528
27529 _cache.invalidated = true; // retire the texture if its utility is low
27530
27531 self.checkTextureUtility(txr);
27532 }
27533 } // remove from queue since the old req was for the old state
27534
27535
27536 self.removeFromQueue(ele);
27537};
27538
27539ETCp.checkTextureUtility = function (txr) {
27540 // invalidate all entries in the cache if the cache size is small
27541 if (txr.invalidatedWidth >= minUtility * txr.width) {
27542 this.retireTexture(txr);
27543 }
27544};
27545
27546ETCp.checkTextureFullness = function (txr) {
27547 // if texture has been mostly filled and passed over several times, remove
27548 // it from the queue so we don't need to waste time looking at it to put new things
27549 var self = this;
27550 var txrQ = self.getTextureQueue(txr.height);
27551
27552 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27553 removeFromArray(txrQ, txr);
27554 } else {
27555 txr.fullnessChecks++;
27556 }
27557};
27558
27559ETCp.retireTexture = function (txr) {
27560 var self = this;
27561 var txrH = txr.height;
27562 var txrQ = self.getTextureQueue(txrH);
27563 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27564
27565 removeFromArray(txrQ, txr);
27566 txr.retired = true; // remove the refs from the eles to the caches:
27567
27568 var eleCaches = txr.eleCaches;
27569
27570 for (var i = 0; i < eleCaches.length; i++) {
27571 var eleCache = eleCaches[i];
27572 lookup.deleteCache(eleCache.key, eleCache.level);
27573 }
27574
27575 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27576
27577 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27578 rtxtrQ.push(txr);
27579};
27580
27581ETCp.addTexture = function (txrH, minW) {
27582 var self = this;
27583 var txrQ = self.getTextureQueue(txrH);
27584 var txr = {};
27585 txrQ.push(txr);
27586 txr.eleCaches = [];
27587 txr.height = txrH;
27588 txr.width = Math.max(defTxrWidth, minW);
27589 txr.usedWidth = 0;
27590 txr.invalidatedWidth = 0;
27591 txr.fullnessChecks = 0;
27592 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27593 txr.context = txr.canvas.getContext('2d');
27594 return txr;
27595};
27596
27597ETCp.recycleTexture = function (txrH, minW) {
27598 var self = this;
27599 var txrQ = self.getTextureQueue(txrH);
27600 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27601
27602 for (var i = 0; i < rtxtrQ.length; i++) {
27603 var txr = rtxtrQ[i];
27604
27605 if (txr.width >= minW) {
27606 txr.retired = false;
27607 txr.usedWidth = 0;
27608 txr.invalidatedWidth = 0;
27609 txr.fullnessChecks = 0;
27610 clearArray(txr.eleCaches);
27611 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27612 txr.context.clearRect(0, 0, txr.width, txr.height);
27613 removeFromArray(rtxtrQ, txr);
27614 txrQ.push(txr);
27615 return txr;
27616 }
27617 }
27618};
27619
27620ETCp.queueElement = function (ele, lvl) {
27621 var self = this;
27622 var q = self.getElementQueue();
27623 var k2q = self.getElementKeyToQueue();
27624 var key = this.getKey(ele);
27625 var existingReq = k2q[key];
27626
27627 if (existingReq) {
27628 // use the max lvl b/c in between lvls are cheap to make
27629 existingReq.level = Math.max(existingReq.level, lvl);
27630 existingReq.eles.merge(ele);
27631 existingReq.reqs++;
27632 q.updateItem(existingReq);
27633 } else {
27634 var req = {
27635 eles: ele.spawn().merge(ele),
27636 level: lvl,
27637 reqs: 1,
27638 key: key
27639 };
27640 q.push(req);
27641 k2q[key] = req;
27642 }
27643};
27644
27645ETCp.dequeue = function (pxRatio
27646/*, extent*/
27647) {
27648 var self = this;
27649 var q = self.getElementQueue();
27650 var k2q = self.getElementKeyToQueue();
27651 var dequeued = [];
27652 var lookup = self.lookup;
27653
27654 for (var i = 0; i < maxDeqSize; i++) {
27655 if (q.size() > 0) {
27656 var req = q.pop();
27657 var key = req.key;
27658 var ele = req.eles[0]; // all eles have the same key
27659
27660 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27661
27662 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27663
27664 if (cacheExists) {
27665 continue;
27666 }
27667
27668 dequeued.push(req);
27669 var bb = self.getBoundingBox(ele);
27670 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27671 } else {
27672 break;
27673 }
27674 }
27675
27676 return dequeued;
27677};
27678
27679ETCp.removeFromQueue = function (ele) {
27680 var self = this;
27681 var q = self.getElementQueue();
27682 var k2q = self.getElementKeyToQueue();
27683 var key = this.getKey(ele);
27684 var req = k2q[key];
27685
27686 if (req != null) {
27687 if (req.eles.length === 1) {
27688 // remove if last ele in the req
27689 // bring to front of queue
27690 req.reqs = MAX_INT;
27691 q.updateItem(req);
27692 q.pop(); // remove from queue
27693
27694 k2q[key] = null; // remove from lookup map
27695 } else {
27696 // otherwise just remove ele from req
27697 req.eles.unmerge(ele);
27698 }
27699 }
27700};
27701
27702ETCp.onDequeue = function (fn) {
27703 this.onDequeues.push(fn);
27704};
27705
27706ETCp.offDequeue = function (fn) {
27707 removeFromArray(this.onDequeues, fn);
27708};
27709
27710ETCp.setupDequeueing = defs.setupDequeueing({
27711 deqRedrawThreshold: deqRedrawThreshold,
27712 deqCost: deqCost,
27713 deqAvgCost: deqAvgCost,
27714 deqNoDrawCost: deqNoDrawCost,
27715 deqFastCost: deqFastCost,
27716 deq: function deq(self, pxRatio, extent) {
27717 return self.dequeue(pxRatio, extent);
27718 },
27719 onDeqd: function onDeqd(self, deqd) {
27720 for (var i = 0; i < self.onDequeues.length; i++) {
27721 var fn = self.onDequeues[i];
27722 fn(deqd);
27723 }
27724 },
27725 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27726 for (var i = 0; i < deqd.length; i++) {
27727 var eles = deqd[i].eles;
27728
27729 for (var j = 0; j < eles.length; j++) {
27730 var bb = eles[j].boundingBox();
27731
27732 if (boundingBoxesIntersect(bb, extent)) {
27733 return true;
27734 }
27735 }
27736 }
27737
27738 return false;
27739 },
27740 priority: function priority(self) {
27741 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27742 }
27743});
27744
27745var defNumLayers = 1; // default number of layers to use
27746
27747var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27748
27749var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
27750
27751var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
27752
27753var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27754
27755var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27756
27757var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27758
27759var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27760
27761var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27762
27763var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27764
27765var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27766
27767var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27768
27769var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27770
27771var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27772// var log = function(){ console.log.apply( console, arguments ); };
27773
27774var LayeredTextureCache = function LayeredTextureCache(renderer) {
27775 var self = this;
27776 var r = self.renderer = renderer;
27777 var cy = r.cy;
27778 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27779
27780 self.firstGet = true;
27781 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27782 self.skipping = false;
27783 self.eleTxrDeqs = cy.collection();
27784 self.scheduleElementRefinement = util(function () {
27785 self.refineElementTextures(self.eleTxrDeqs);
27786 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27787 }, refineEleDebounceTime);
27788 r.beforeRender(function (willDraw, now) {
27789 if (now - self.lastInvalidationTime <= invalidThreshold) {
27790 self.skipping = true;
27791 } else {
27792 self.skipping = false;
27793 }
27794 }, r.beforeRenderPriorities.lyrTxrSkip);
27795
27796 var qSort = function qSort(a, b) {
27797 return b.reqs - a.reqs;
27798 };
27799
27800 self.layersQueue = new Heap(qSort);
27801 self.setupDequeueing();
27802};
27803
27804var LTCp = LayeredTextureCache.prototype;
27805var layerIdPool = 0;
27806var MAX_INT$1 = Math.pow(2, 53) - 1;
27807
27808LTCp.makeLayer = function (bb, lvl) {
27809 var scale = Math.pow(2, lvl);
27810 var w = Math.ceil(bb.w * scale);
27811 var h = Math.ceil(bb.h * scale);
27812 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27813 var layer = {
27814 id: layerIdPool = ++layerIdPool % MAX_INT$1,
27815 bb: bb,
27816 level: lvl,
27817 width: w,
27818 height: h,
27819 canvas: canvas,
27820 context: canvas.getContext('2d'),
27821 eles: [],
27822 elesQueue: [],
27823 reqs: 0
27824 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27825
27826 var cxt = layer.context;
27827 var dx = -layer.bb.x1;
27828 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
27829
27830 cxt.scale(scale, scale);
27831 cxt.translate(dx, dy);
27832 return layer;
27833};
27834
27835LTCp.getLayers = function (eles, pxRatio, lvl) {
27836 var self = this;
27837 var r = self.renderer;
27838 var cy = r.cy;
27839 var zoom = cy.zoom();
27840 var firstGet = self.firstGet;
27841 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
27842 //log eles.map(function(ele){ return ele.id() }) );
27843
27844 if (lvl == null) {
27845 lvl = Math.ceil(log2(zoom * pxRatio));
27846
27847 if (lvl < minLvl$1) {
27848 lvl = minLvl$1;
27849 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27850 return null;
27851 }
27852 }
27853
27854 self.validateLayersElesOrdering(lvl, eles);
27855 var layersByLvl = self.layersByLevel;
27856 var scale = Math.pow(2, lvl);
27857 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
27858 var bb;
27859 var lvlComplete = self.levelIsComplete(lvl, eles);
27860 var tmpLayers;
27861
27862 var checkTempLevels = function checkTempLevels() {
27863 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
27864 self.validateLayersElesOrdering(l, eles);
27865
27866 if (self.levelIsComplete(l, eles)) {
27867 tmpLayers = layersByLvl[l];
27868 return true;
27869 }
27870 };
27871
27872 var checkLvls = function checkLvls(dir) {
27873 if (tmpLayers) {
27874 return;
27875 }
27876
27877 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
27878 if (canUseAsTmpLvl(l)) {
27879 break;
27880 }
27881 }
27882 };
27883
27884 checkLvls(+1);
27885 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
27886
27887 for (var i = layers.length - 1; i >= 0; i--) {
27888 var layer = layers[i];
27889
27890 if (layer.invalid) {
27891 removeFromArray(layers, layer);
27892 }
27893 }
27894 };
27895
27896 if (!lvlComplete) {
27897 // if the current level is incomplete, then use the closest, best quality layerset temporarily
27898 // and later queue the current layerset so we can get the proper quality level soon
27899 checkTempLevels();
27900 } else {
27901 // log('level complete, using existing layers\n--');
27902 return layers;
27903 }
27904
27905 var getBb = function getBb() {
27906 if (!bb) {
27907 bb = makeBoundingBox();
27908
27909 for (var i = 0; i < eles.length; i++) {
27910 updateBoundingBox(bb, eles[i].boundingBox());
27911 }
27912 }
27913
27914 return bb;
27915 };
27916
27917 var makeLayer = function makeLayer(opts) {
27918 opts = opts || {};
27919 var after = opts.after;
27920 getBb();
27921 var area = bb.w * scale * (bb.h * scale);
27922
27923 if (area > maxLayerArea) {
27924 return null;
27925 }
27926
27927 var layer = self.makeLayer(bb, lvl);
27928
27929 if (after != null) {
27930 var index = layers.indexOf(after) + 1;
27931 layers.splice(index, 0, layer);
27932 } else if (opts.insert === undefined || opts.insert) {
27933 // no after specified => first layer made so put at start
27934 layers.unshift(layer);
27935 } // if( tmpLayers ){
27936 //self.queueLayer( layer );
27937 // }
27938
27939
27940 return layer;
27941 };
27942
27943 if (self.skipping && !firstGet) {
27944 // log('skip layers');
27945 return null;
27946 } // log('do layers');
27947
27948
27949 var layer = null;
27950 var maxElesPerLayer = eles.length / defNumLayers;
27951 var allowLazyQueueing = !firstGet;
27952
27953 for (var i = 0; i < eles.length; i++) {
27954 var ele = eles[i];
27955 var rs = ele._private.rscratch;
27956 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
27957
27958 var existingLayer = caches[lvl];
27959
27960 if (existingLayer) {
27961 // reuse layer for later eles
27962 // log('reuse layer for', ele.id());
27963 layer = existingLayer;
27964 continue;
27965 }
27966
27967 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
27968 // log('make new layer for ele %s', ele.id());
27969 layer = makeLayer({
27970 insert: true,
27971 after: layer
27972 }); // if now layer can be built then we can't use layers at this level
27973
27974 if (!layer) {
27975 return null;
27976 } // log('new layer with id %s', layer.id);
27977
27978 }
27979
27980 if (tmpLayers || allowLazyQueueing) {
27981 // log('queue ele %s in layer %s', ele.id(), layer.id);
27982 self.queueLayer(layer, ele);
27983 } else {
27984 // log('draw ele %s in layer %s', ele.id(), layer.id);
27985 self.drawEleInLayer(layer, ele, lvl, pxRatio);
27986 }
27987
27988 layer.eles.push(ele);
27989 caches[lvl] = layer;
27990 } // log('--');
27991
27992
27993 if (tmpLayers) {
27994 // then we only queued the current layerset and can't draw it yet
27995 return tmpLayers;
27996 }
27997
27998 if (allowLazyQueueing) {
27999 // log('lazy queue level', lvl);
28000 return null;
28001 }
28002
28003 return layers;
28004}; // a layer may want to use an ele cache of a higher level to avoid blurriness
28005// so the layer level might not equal the ele level
28006
28007
28008LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
28009 return lvl;
28010};
28011
28012LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
28013 var self = this;
28014 var r = this.renderer;
28015 var context = layer.context;
28016 var bb = ele.boundingBox();
28017
28018 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28019 return;
28020 }
28021
28022 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
28023
28024 {
28025 r.setImgSmoothing(context, false);
28026 }
28027
28028 {
28029 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
28030 }
28031
28032 {
28033 r.setImgSmoothing(context, true);
28034 }
28035};
28036
28037LTCp.levelIsComplete = function (lvl, eles) {
28038 var self = this;
28039 var layers = self.layersByLevel[lvl];
28040
28041 if (!layers || layers.length === 0) {
28042 return false;
28043 }
28044
28045 var numElesInLayers = 0;
28046
28047 for (var i = 0; i < layers.length; i++) {
28048 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28049
28050 if (layer.reqs > 0) {
28051 return false;
28052 } // if the layer is invalid, the level is not complete
28053
28054
28055 if (layer.invalid) {
28056 return false;
28057 }
28058
28059 numElesInLayers += layer.eles.length;
28060 } // we should have exactly the number of eles passed in to be complete
28061
28062
28063 if (numElesInLayers !== eles.length) {
28064 return false;
28065 }
28066
28067 return true;
28068};
28069
28070LTCp.validateLayersElesOrdering = function (lvl, eles) {
28071 var layers = this.layersByLevel[lvl];
28072
28073 if (!layers) {
28074 return;
28075 } // if in a layer the eles are not in the same order, then the layer is invalid
28076 // (i.e. there is an ele in between the eles in the layer)
28077
28078
28079 for (var i = 0; i < layers.length; i++) {
28080 var layer = layers[i];
28081 var offset = -1; // find the offset
28082
28083 for (var j = 0; j < eles.length; j++) {
28084 if (layer.eles[0] === eles[j]) {
28085 offset = j;
28086 break;
28087 }
28088 }
28089
28090 if (offset < 0) {
28091 // then the layer has nonexistant elements and is invalid
28092 this.invalidateLayer(layer);
28093 continue;
28094 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28095
28096
28097 var o = offset;
28098
28099 for (var j = 0; j < layer.eles.length; j++) {
28100 if (layer.eles[j] !== eles[o + j]) {
28101 // log('invalidate based on ordering', layer.id);
28102 this.invalidateLayer(layer);
28103 break;
28104 }
28105 }
28106 }
28107};
28108
28109LTCp.updateElementsInLayers = function (eles, update) {
28110 var self = this;
28111 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28112 // layer itself along the way
28113
28114 for (var i = 0; i < eles.length; i++) {
28115 var req = isEles ? null : eles[i];
28116 var ele = isEles ? eles[i] : eles[i].ele;
28117 var rs = ele._private.rscratch;
28118 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28119
28120 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28121 var layer = caches[l];
28122
28123 if (!layer) {
28124 continue;
28125 } // if update is a request from the ele cache, then it affects only
28126 // the matching level
28127
28128
28129 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28130 continue;
28131 }
28132
28133 update(layer, ele, req);
28134 }
28135 }
28136};
28137
28138LTCp.haveLayers = function () {
28139 var self = this;
28140 var haveLayers = false;
28141
28142 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28143 var layers = self.layersByLevel[l];
28144
28145 if (layers && layers.length > 0) {
28146 haveLayers = true;
28147 break;
28148 }
28149 }
28150
28151 return haveLayers;
28152};
28153
28154LTCp.invalidateElements = function (eles) {
28155 var self = this;
28156
28157 if (eles.length === 0) {
28158 return;
28159 }
28160
28161 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28162
28163 if (eles.length === 0 || !self.haveLayers()) {
28164 return;
28165 }
28166
28167 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28168 self.invalidateLayer(layer);
28169 });
28170};
28171
28172LTCp.invalidateLayer = function (layer) {
28173 // log('update invalidate layer time');
28174 this.lastInvalidationTime = performanceNow();
28175
28176 if (layer.invalid) {
28177 return;
28178 } // save cycles
28179
28180
28181 var lvl = layer.level;
28182 var eles = layer.eles;
28183 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28184
28185 removeFromArray(layers, layer); // layer.eles = [];
28186
28187 layer.elesQueue = [];
28188 layer.invalid = true;
28189
28190 if (layer.replacement) {
28191 layer.replacement.invalid = true;
28192 }
28193
28194 for (var i = 0; i < eles.length; i++) {
28195 var caches = eles[i]._private.rscratch.imgLayerCaches;
28196
28197 if (caches) {
28198 caches[lvl] = null;
28199 }
28200 }
28201};
28202
28203LTCp.refineElementTextures = function (eles) {
28204 var self = this; // log('refine', eles.length);
28205
28206 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28207 var rLyr = layer.replacement;
28208
28209 if (!rLyr) {
28210 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28211 rLyr.replaces = layer;
28212 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28213 }
28214
28215 if (!rLyr.reqs) {
28216 for (var i = 0; i < rLyr.eles.length; i++) {
28217 self.queueLayer(rLyr, rLyr.eles[i]);
28218 } // log('queue replacement layer refinement', rLyr.id);
28219
28220 }
28221 });
28222};
28223
28224LTCp.enqueueElementRefinement = function (ele) {
28225
28226 this.eleTxrDeqs.merge(ele);
28227 this.scheduleElementRefinement();
28228};
28229
28230LTCp.queueLayer = function (layer, ele) {
28231 var self = this;
28232 var q = self.layersQueue;
28233 var elesQ = layer.elesQueue;
28234 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28235
28236 if (layer.replacement) {
28237 return;
28238 }
28239
28240 if (ele) {
28241 if (hasId[ele.id()]) {
28242 return;
28243 }
28244
28245 elesQ.push(ele);
28246 hasId[ele.id()] = true;
28247 }
28248
28249 if (layer.reqs) {
28250 layer.reqs++;
28251 q.updateItem(layer);
28252 } else {
28253 layer.reqs = 1;
28254 q.push(layer);
28255 }
28256};
28257
28258LTCp.dequeue = function (pxRatio) {
28259 var self = this;
28260 var q = self.layersQueue;
28261 var deqd = [];
28262 var eleDeqs = 0;
28263
28264 while (eleDeqs < maxDeqSize$1) {
28265 if (q.size() === 0) {
28266 break;
28267 }
28268
28269 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28270
28271 if (layer.replacement) {
28272 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28273 q.pop();
28274 continue;
28275 } // if this is a replacement layer that has been superceded, then forget it
28276
28277
28278 if (layer.replaces && layer !== layer.replaces.replacement) {
28279 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28280 q.pop();
28281 continue;
28282 }
28283
28284 if (layer.invalid) {
28285 // log('replacement layer %s is invalid; dequeued', layer.id);
28286 q.pop();
28287 continue;
28288 }
28289
28290 var ele = layer.elesQueue.shift();
28291
28292 if (ele) {
28293 // log('dequeue layer %s', layer.id);
28294 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28295 eleDeqs++;
28296 }
28297
28298 if (deqd.length === 0) {
28299 // we need only one entry in deqd to queue redrawing etc
28300 deqd.push(true);
28301 } // if the layer has all its eles done, then remove from the queue
28302
28303
28304 if (layer.elesQueue.length === 0) {
28305 q.pop();
28306 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28307 // when a replacement layer is dequeued, it replaces the old layer in the level
28308
28309 if (layer.replaces) {
28310 self.applyLayerReplacement(layer);
28311 }
28312
28313 self.requestRedraw();
28314 }
28315 }
28316
28317 return deqd;
28318};
28319
28320LTCp.applyLayerReplacement = function (layer) {
28321 var self = this;
28322 var layersInLevel = self.layersByLevel[layer.level];
28323 var replaced = layer.replaces;
28324 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28325 // refs would be a mistake (i.e. overwriting the true active layer)
28326
28327 if (index < 0 || replaced.invalid) {
28328 // log('replacement layer would have no effect', layer.id);
28329 return;
28330 }
28331
28332 layersInLevel[index] = layer; // replace level ref
28333 // replace refs in eles
28334
28335 for (var i = 0; i < layer.eles.length; i++) {
28336 var _p = layer.eles[i]._private;
28337 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28338
28339 if (cache) {
28340 cache[layer.level] = layer;
28341 }
28342 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
28343
28344
28345 self.requestRedraw();
28346};
28347
28348LTCp.requestRedraw = util(function () {
28349 var r = this.renderer;
28350 r.redrawHint('eles', true);
28351 r.redrawHint('drag', true);
28352 r.redraw();
28353}, 100);
28354LTCp.setupDequeueing = defs.setupDequeueing({
28355 deqRedrawThreshold: deqRedrawThreshold$1,
28356 deqCost: deqCost$1,
28357 deqAvgCost: deqAvgCost$1,
28358 deqNoDrawCost: deqNoDrawCost$1,
28359 deqFastCost: deqFastCost$1,
28360 deq: function deq(self, pxRatio) {
28361 return self.dequeue(pxRatio);
28362 },
28363 onDeqd: noop,
28364 shouldRedraw: trueify,
28365 priority: function priority(self) {
28366 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28367 }
28368});
28369
28370var CRp = {};
28371var impl;
28372
28373function polygon(context, points) {
28374 for (var i = 0; i < points.length; i++) {
28375 var pt = points[i];
28376 context.lineTo(pt.x, pt.y);
28377 }
28378}
28379
28380function triangleBackcurve(context, points, controlPoint) {
28381 var firstPt;
28382
28383 for (var i = 0; i < points.length; i++) {
28384 var pt = points[i];
28385
28386 if (i === 0) {
28387 firstPt = pt;
28388 }
28389
28390 context.lineTo(pt.x, pt.y);
28391 }
28392
28393 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28394}
28395
28396function triangleTee(context, trianglePoints, teePoints) {
28397 if (context.beginPath) {
28398 context.beginPath();
28399 }
28400
28401 var triPts = trianglePoints;
28402
28403 for (var i = 0; i < triPts.length; i++) {
28404 var pt = triPts[i];
28405 context.lineTo(pt.x, pt.y);
28406 }
28407
28408 var teePts = teePoints;
28409 var firstTeePt = teePoints[0];
28410 context.moveTo(firstTeePt.x, firstTeePt.y);
28411
28412 for (var i = 1; i < teePts.length; i++) {
28413 var pt = teePts[i];
28414 context.lineTo(pt.x, pt.y);
28415 }
28416
28417 if (context.closePath) {
28418 context.closePath();
28419 }
28420}
28421
28422function circleTriangle(context, trianglePoints, rx, ry, r) {
28423 if (context.beginPath) {
28424 context.beginPath();
28425 }
28426
28427 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28428 var triPts = trianglePoints;
28429 var firstTrPt = triPts[0];
28430 context.moveTo(firstTrPt.x, firstTrPt.y);
28431
28432 for (var i = 0; i < triPts.length; i++) {
28433 var pt = triPts[i];
28434 context.lineTo(pt.x, pt.y);
28435 }
28436
28437 if (context.closePath) {
28438 context.closePath();
28439 }
28440}
28441
28442function circle(context, rx, ry, r) {
28443 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28444}
28445
28446CRp.arrowShapeImpl = function (name) {
28447 return (impl || (impl = {
28448 'polygon': polygon,
28449 'triangle-backcurve': triangleBackcurve,
28450 'triangle-tee': triangleTee,
28451 'circle-triangle': circleTriangle,
28452 'triangle-cross': triangleTee,
28453 'circle': circle
28454 }))[name];
28455};
28456
28457var CRp$1 = {};
28458
28459CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28460 var r = this;
28461
28462 if (ele.isNode()) {
28463 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28464 } else {
28465 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28466 }
28467};
28468
28469CRp$1.drawElementOverlay = function (context, ele) {
28470 var r = this;
28471
28472 if (ele.isNode()) {
28473 r.drawNodeOverlay(context, ele);
28474 } else {
28475 r.drawEdgeOverlay(context, ele);
28476 }
28477};
28478
28479CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28480 var r = this;
28481 var bb = eleTxrCache.getBoundingBox(ele);
28482
28483 if (bb.w === 0 || bb.h === 0) {
28484 return;
28485 } // ignore zero size case
28486
28487
28488 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28489
28490 if (eleCache != null) {
28491 var opacity = getOpacity(r, ele);
28492
28493 if (opacity === 0) {
28494 return;
28495 }
28496
28497 var theta = getRotation(r, ele);
28498 var x1 = bb.x1,
28499 y1 = bb.y1,
28500 w = bb.w,
28501 h = bb.h;
28502 var x, y, sx, sy, smooth;
28503
28504 if (theta !== 0) {
28505 var rotPt = eleTxrCache.getRotationPoint(ele);
28506 sx = rotPt.x;
28507 sy = rotPt.y;
28508 context.translate(sx, sy);
28509 context.rotate(theta);
28510 smooth = r.getImgSmoothing(context);
28511
28512 if (!smooth) {
28513 r.setImgSmoothing(context, true);
28514 }
28515
28516 var off = eleTxrCache.getRotationOffset(ele);
28517 x = off.x;
28518 y = off.y;
28519 } else {
28520 x = x1;
28521 y = y1;
28522 }
28523
28524 var oldGlobalAlpha;
28525
28526 if (opacity !== 1) {
28527 oldGlobalAlpha = context.globalAlpha;
28528 context.globalAlpha = oldGlobalAlpha * opacity;
28529 }
28530
28531 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28532
28533 if (opacity !== 1) {
28534 context.globalAlpha = oldGlobalAlpha;
28535 }
28536
28537 if (theta !== 0) {
28538 context.rotate(-theta);
28539 context.translate(-sx, -sy);
28540
28541 if (!smooth) {
28542 r.setImgSmoothing(context, false);
28543 }
28544 }
28545 } else {
28546 eleTxrCache.drawElement(context, ele); // direct draw fallback
28547 }
28548};
28549
28550var getZeroRotation = function getZeroRotation() {
28551 return 0;
28552};
28553
28554var getLabelRotation = function getLabelRotation(r, ele) {
28555 return r.getTextAngle(ele, null);
28556};
28557
28558var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28559 return r.getTextAngle(ele, 'source');
28560};
28561
28562var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28563 return r.getTextAngle(ele, 'target');
28564};
28565
28566var getOpacity = function getOpacity(r, ele) {
28567 return ele.effectiveOpacity();
28568};
28569
28570var getTextOpacity = function getTextOpacity(e, ele) {
28571 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28572};
28573
28574CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28575 var r = this;
28576 var _r$data = r.data,
28577 eleTxrCache = _r$data.eleTxrCache,
28578 lblTxrCache = _r$data.lblTxrCache,
28579 slbTxrCache = _r$data.slbTxrCache,
28580 tlbTxrCache = _r$data.tlbTxrCache;
28581 var bb = ele.boundingBox();
28582 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28583
28584 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28585 return;
28586 }
28587
28588 if (!extent || boundingBoxesIntersect(bb, extent)) {
28589 var isEdge = ele.isEdge();
28590
28591 var badLine = ele.element()._private.rscratch.badLine;
28592
28593 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28594
28595 if (!isEdge || !badLine) {
28596 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28597 }
28598
28599 if (isEdge && !badLine) {
28600 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28601 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28602 }
28603
28604 r.drawElementOverlay(context, ele);
28605 }
28606};
28607
28608CRp$1.drawElements = function (context, eles) {
28609 var r = this;
28610
28611 for (var i = 0; i < eles.length; i++) {
28612 var ele = eles[i];
28613 r.drawElement(context, ele);
28614 }
28615};
28616
28617CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
28618 var r = this;
28619
28620 for (var i = 0; i < eles.length; i++) {
28621 var ele = eles[i];
28622 r.drawCachedElement(context, ele, pxRatio, extent);
28623 }
28624};
28625
28626CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
28627 var r = this;
28628
28629 for (var i = 0; i < eles.length; i++) {
28630 var ele = eles[i];
28631
28632 if (!ele.isNode()) {
28633 continue;
28634 }
28635
28636 r.drawCachedElement(context, ele, pxRatio, extent);
28637 }
28638};
28639
28640CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
28641 var r = this;
28642 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28643
28644 if (layers) {
28645 for (var i = 0; i < layers.length; i++) {
28646 var layer = layers[i];
28647 var bb = layer.bb;
28648
28649 if (bb.w === 0 || bb.h === 0) {
28650 continue;
28651 }
28652
28653 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28654 }
28655 } else {
28656 // fall back on plain caching if no layers
28657 r.drawCachedElements(context, eles, pxRatio, extent);
28658 }
28659};
28660
28661/* global Path2D */
28662var CRp$2 = {};
28663
28664CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
28665 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28666 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28667 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28668 var r = this;
28669 var rs = edge._private.rscratch;
28670
28671 if (shouldDrawOpacity && !edge.visible()) {
28672 return;
28673 } // if bezier ctrl pts can not be calculated, then die
28674
28675
28676 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28677 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28678 return;
28679 }
28680
28681 var bb;
28682
28683 if (shiftToOriginWithBb) {
28684 bb = shiftToOriginWithBb;
28685 context.translate(-bb.x1, -bb.y1);
28686 }
28687
28688 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28689 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
28690 var lineStyle = edge.pstyle('line-style').value;
28691 var edgeWidth = edge.pstyle('width').pfValue;
28692 var lineCap = edge.pstyle('line-cap').value;
28693 var effectiveLineOpacity = opacity * lineOpacity; // separate arrow opacity would require arrow-opacity property
28694
28695 var effectiveArrowOpacity = opacity * lineOpacity;
28696
28697 var drawLine = function drawLine() {
28698 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28699 context.lineWidth = edgeWidth;
28700 context.lineCap = lineCap;
28701 r.eleStrokeStyle(context, edge, strokeOpacity);
28702 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28703 context.lineCap = 'butt'; // reset for other drawing functions
28704 };
28705
28706 var drawOverlay = function drawOverlay() {
28707 if (!shouldDrawOverlay) {
28708 return;
28709 }
28710
28711 r.drawEdgeOverlay(context, edge);
28712 };
28713
28714 var drawArrows = function drawArrows() {
28715 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
28716 r.drawArrowheads(context, edge, arrowOpacity);
28717 };
28718
28719 var drawText = function drawText() {
28720 r.drawElementText(context, edge, null, drawLabel);
28721 };
28722
28723 context.lineJoin = 'round';
28724 var ghost = edge.pstyle('ghost').value === 'yes';
28725
28726 if (ghost) {
28727 var gx = edge.pstyle('ghost-offset-x').pfValue;
28728 var gy = edge.pstyle('ghost-offset-y').pfValue;
28729 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28730 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
28731 context.translate(gx, gy);
28732 drawLine(effectiveGhostOpacity);
28733 drawArrows(effectiveGhostOpacity);
28734 context.translate(-gx, -gy);
28735 }
28736
28737 drawLine();
28738 drawArrows();
28739 drawOverlay();
28740 drawText();
28741
28742 if (shiftToOriginWithBb) {
28743 context.translate(bb.x1, bb.y1);
28744 }
28745};
28746
28747CRp$2.drawEdgeOverlay = function (context, edge) {
28748 if (!edge.visible()) {
28749 return;
28750 }
28751
28752 var overlayOpacity = edge.pstyle('overlay-opacity').value;
28753
28754 if (overlayOpacity === 0) {
28755 return;
28756 }
28757
28758 var r = this;
28759 var usePaths = r.usePaths();
28760 var rs = edge._private.rscratch;
28761 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
28762 var overlayWidth = 2 * overlayPadding;
28763 var overlayColor = edge.pstyle('overlay-color').value;
28764 context.lineWidth = overlayWidth;
28765
28766 if (rs.edgeType === 'self' && !usePaths) {
28767 context.lineCap = 'butt';
28768 } else {
28769 context.lineCap = 'round';
28770 }
28771
28772 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
28773 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28774};
28775
28776CRp$2.drawEdgePath = function (edge, context, pts, type) {
28777 var rs = edge._private.rscratch;
28778 var canvasCxt = context;
28779 var path;
28780 var pathCacheHit = false;
28781 var usePaths = this.usePaths();
28782 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28783 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28784
28785 if (usePaths) {
28786 var pathCacheKey = pts.join('$');
28787 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28788
28789 if (keyMatches) {
28790 path = context = rs.pathCache;
28791 pathCacheHit = true;
28792 } else {
28793 path = context = new Path2D();
28794 rs.pathCacheKey = pathCacheKey;
28795 rs.pathCache = path;
28796 }
28797 }
28798
28799 if (canvasCxt.setLineDash) {
28800 // for very outofdate browsers
28801 switch (type) {
28802 case 'dotted':
28803 canvasCxt.setLineDash([1, 1]);
28804 break;
28805
28806 case 'dashed':
28807 canvasCxt.setLineDash(lineDashPattern);
28808 canvasCxt.lineDashOffset = lineDashOffset;
28809 break;
28810
28811 case 'solid':
28812 canvasCxt.setLineDash([]);
28813 break;
28814 }
28815 }
28816
28817 if (!pathCacheHit && !rs.badLine) {
28818 if (context.beginPath) {
28819 context.beginPath();
28820 }
28821
28822 context.moveTo(pts[0], pts[1]);
28823
28824 switch (rs.edgeType) {
28825 case 'bezier':
28826 case 'self':
28827 case 'compound':
28828 case 'multibezier':
28829 for (var i = 2; i + 3 < pts.length; i += 4) {
28830 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
28831 }
28832
28833 break;
28834
28835 case 'straight':
28836 case 'segments':
28837 case 'haystack':
28838 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
28839 context.lineTo(pts[_i], pts[_i + 1]);
28840 }
28841
28842 break;
28843 }
28844 }
28845
28846 context = canvasCxt;
28847
28848 if (usePaths) {
28849 context.stroke(path);
28850 } else {
28851 context.stroke();
28852 } // reset any line dashes
28853
28854
28855 if (context.setLineDash) {
28856 // for very outofdate browsers
28857 context.setLineDash([]);
28858 }
28859};
28860
28861CRp$2.drawArrowheads = function (context, edge, opacity) {
28862 var rs = edge._private.rscratch;
28863 var isHaystack = rs.edgeType === 'haystack';
28864
28865 if (!isHaystack) {
28866 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
28867 }
28868
28869 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
28870 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
28871
28872 if (!isHaystack) {
28873 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
28874 }
28875};
28876
28877CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
28878 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
28879 return;
28880 }
28881
28882 var self = this;
28883 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
28884
28885 if (arrowShape === 'none') {
28886 return;
28887 }
28888
28889 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
28890 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
28891 var edgeWidth = edge.pstyle('width').pfValue;
28892 var edgeOpacity = edge.pstyle('opacity').value;
28893
28894 if (opacity === undefined) {
28895 opacity = edgeOpacity;
28896 }
28897
28898 var gco = context.globalCompositeOperation;
28899
28900 if (opacity !== 1 || arrowFill === 'hollow') {
28901 // then extra clear is needed
28902 context.globalCompositeOperation = 'destination-out';
28903 self.colorFillStyle(context, 255, 255, 255, 1);
28904 self.colorStrokeStyle(context, 255, 255, 255, 1);
28905 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
28906 context.globalCompositeOperation = gco;
28907 } // otherwise, the opaque arrow clears it for free :)
28908
28909
28910 var color = edge.pstyle(prefix + '-arrow-color').value;
28911 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
28912 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28913 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
28914};
28915
28916CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
28917 var r = this;
28918 var usePaths = this.usePaths() && shape !== 'triangle-cross';
28919 var pathCacheHit = false;
28920 var path;
28921 var canvasContext = context;
28922 var translation = {
28923 x: x,
28924 y: y
28925 };
28926 var scale = edge.pstyle('arrow-scale').value;
28927 var size = this.getArrowWidth(edgeWidth, scale);
28928 var shapeImpl = r.arrowShapes[shape];
28929
28930 if (usePaths) {
28931 var cache = r.arrowPathCache = r.arrowPathCache || [];
28932 var key = hashString(shape);
28933 var cachedPath = cache[key];
28934
28935 if (cachedPath != null) {
28936 path = context = cachedPath;
28937 pathCacheHit = true;
28938 } else {
28939 path = context = new Path2D();
28940 cache[key] = path;
28941 }
28942 }
28943
28944 if (!pathCacheHit) {
28945 if (context.beginPath) {
28946 context.beginPath();
28947 }
28948
28949 if (usePaths) {
28950 // store in the path cache with values easily manipulated later
28951 shapeImpl.draw(context, 1, 0, {
28952 x: 0,
28953 y: 0
28954 }, 1);
28955 } else {
28956 shapeImpl.draw(context, size, angle, translation, edgeWidth);
28957 }
28958
28959 if (context.closePath) {
28960 context.closePath();
28961 }
28962 }
28963
28964 context = canvasContext;
28965
28966 if (usePaths) {
28967 // set transform to arrow position/orientation
28968 context.translate(x, y);
28969 context.rotate(angle);
28970 context.scale(size, size);
28971 }
28972
28973 if (fill === 'filled' || fill === 'both') {
28974 if (usePaths) {
28975 context.fill(path);
28976 } else {
28977 context.fill();
28978 }
28979 }
28980
28981 if (fill === 'hollow' || fill === 'both') {
28982 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
28983 context.lineJoin = 'miter';
28984
28985 if (usePaths) {
28986 context.stroke(path);
28987 } else {
28988 context.stroke();
28989 }
28990 }
28991
28992 if (usePaths) {
28993 // reset transform by applying inverse
28994 context.scale(1 / size, 1 / size);
28995 context.rotate(-angle);
28996 context.translate(-x, -y);
28997 }
28998};
28999
29000var CRp$3 = {};
29001
29002CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
29003 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
29004 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
29005 return;
29006 }
29007
29008 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
29009};
29010
29011CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
29012 var r = this;
29013 var pos = node.position();
29014 var nodeX = pos.x;
29015 var nodeY = pos.y;
29016 var styleObj = node.cy().style();
29017 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
29018 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
29019 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
29020 var nodeW = node.width();
29021 var nodeH = node.height();
29022 var paddingX2 = node.padding() * 2;
29023 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29024 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29025 var rs = node._private.rscratch;
29026 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
29027 var shouldClip = clip === 'node';
29028 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
29029 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
29030 var imgW = img.width || img.cachedW;
29031 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
29032
29033 if (null == imgW || null == imgH) {
29034 document.body.appendChild(img); // eslint-disable-line no-undef
29035
29036 imgW = img.cachedW = img.width || img.offsetWidth;
29037 imgH = img.cachedH = img.height || img.offsetHeight;
29038 document.body.removeChild(img); // eslint-disable-line no-undef
29039 }
29040
29041 var w = imgW;
29042 var h = imgH;
29043
29044 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29045 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29046 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29047 } else {
29048 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29049 }
29050 }
29051
29052 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29053 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29054 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29055 } else {
29056 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29057 }
29058 }
29059
29060 if (w === 0 || h === 0) {
29061 return; // no point in drawing empty image (and chrome is broken in this case)
29062 }
29063
29064 if (fit === 'contain') {
29065 var scale = Math.min(nodeTW / w, nodeTH / h);
29066 w *= scale;
29067 h *= scale;
29068 } else if (fit === 'cover') {
29069 var scale = Math.max(nodeTW / w, nodeTH / h);
29070 w *= scale;
29071 h *= scale;
29072 }
29073
29074 var x = nodeX - nodeTW / 2; // left
29075
29076 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29077 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29078
29079 if (posXUnits === '%') {
29080 x += (nodeTW - w) * posXPfVal;
29081 } else {
29082 x += posXPfVal;
29083 }
29084
29085 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29086 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29087
29088 if (offXUnits === '%') {
29089 x += (nodeTW - w) * offXPfVal;
29090 } else {
29091 x += offXPfVal;
29092 }
29093
29094 var y = nodeY - nodeTH / 2; // top
29095
29096 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29097 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29098
29099 if (posYUnits === '%') {
29100 y += (nodeTH - h) * posYPfVal;
29101 } else {
29102 y += posYPfVal;
29103 }
29104
29105 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29106 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29107
29108 if (offYUnits === '%') {
29109 y += (nodeTH - h) * offYPfVal;
29110 } else {
29111 y += offYPfVal;
29112 }
29113
29114 if (rs.pathCache) {
29115 x -= nodeX;
29116 y -= nodeY;
29117 nodeX = 0;
29118 nodeY = 0;
29119 }
29120
29121 var gAlpha = context.globalAlpha;
29122 context.globalAlpha = imgOpacity;
29123 var smoothingEnabled = r.getImgSmoothing(context);
29124 var isSmoothingSwitched = false;
29125
29126 if (smooth === 'no' && smoothingEnabled) {
29127 r.setImgSmoothing(context, false);
29128 isSmoothingSwitched = true;
29129 } else if (smooth === 'yes' && !smoothingEnabled) {
29130 r.setImgSmoothing(context, true);
29131 isSmoothingSwitched = true;
29132 }
29133
29134 if (repeat === 'no-repeat') {
29135 if (shouldClip) {
29136 context.save();
29137
29138 if (rs.pathCache) {
29139 context.clip(rs.pathCache);
29140 } else {
29141 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29142 context.clip();
29143 }
29144 }
29145
29146 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29147
29148 if (shouldClip) {
29149 context.restore();
29150 }
29151 } else {
29152 var pattern = context.createPattern(img, repeat);
29153 context.fillStyle = pattern;
29154 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29155 context.translate(x, y);
29156 context.fill();
29157 context.translate(-x, -y);
29158 }
29159
29160 context.globalAlpha = gAlpha;
29161
29162 if (isSmoothingSwitched) {
29163 r.setImgSmoothing(context, smoothingEnabled);
29164 }
29165};
29166
29167var CRp$4 = {};
29168
29169CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29170 if (!scale) {
29171 var zoom = ele.cy().zoom();
29172 var pxRatio = this.getPixelRatio();
29173 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29174
29175 scale = Math.pow(2, lvl);
29176 }
29177
29178 var computedSize = ele.pstyle('font-size').pfValue * scale;
29179 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29180
29181 if (computedSize < minSize) {
29182 return false;
29183 }
29184
29185 return true;
29186};
29187
29188CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29189 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29190 var r = this;
29191
29192 if (force == null) {
29193 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29194 return;
29195 }
29196 } else if (force === false) {
29197 return;
29198 }
29199
29200 if (ele.isNode()) {
29201 var label = ele.pstyle('label');
29202
29203 if (!label || !label.value) {
29204 return;
29205 }
29206
29207 var justification = r.getLabelJustification(ele);
29208 context.textAlign = justification;
29209 context.textBaseline = 'bottom';
29210 } else {
29211 var badLine = ele.element()._private.rscratch.badLine;
29212
29213 var _label = ele.pstyle('label');
29214
29215 var srcLabel = ele.pstyle('source-label');
29216 var tgtLabel = ele.pstyle('target-label');
29217
29218 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29219 return;
29220 }
29221
29222 context.textAlign = 'center';
29223 context.textBaseline = 'bottom';
29224 }
29225
29226 var applyRotation = !shiftToOriginWithBb;
29227 var bb;
29228
29229 if (shiftToOriginWithBb) {
29230 bb = shiftToOriginWithBb;
29231 context.translate(-bb.x1, -bb.y1);
29232 }
29233
29234 if (prefix == null) {
29235 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29236
29237 if (ele.isEdge()) {
29238 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29239 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29240 }
29241 } else {
29242 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29243 }
29244
29245 if (shiftToOriginWithBb) {
29246 context.translate(bb.x1, bb.y1);
29247 }
29248};
29249
29250CRp$4.getFontCache = function (context) {
29251 var cache;
29252 this.fontCaches = this.fontCaches || [];
29253
29254 for (var i = 0; i < this.fontCaches.length; i++) {
29255 cache = this.fontCaches[i];
29256
29257 if (cache.context === context) {
29258 return cache;
29259 }
29260 }
29261
29262 cache = {
29263 context: context
29264 };
29265 this.fontCaches.push(cache);
29266 return cache;
29267}; // set up canvas context with font
29268// returns transformed text string
29269
29270
29271CRp$4.setupTextStyle = function (context, ele) {
29272 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29273 // Font style
29274 var labelStyle = ele.pstyle('font-style').strValue;
29275 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29276 var labelFamily = ele.pstyle('font-family').strValue;
29277 var labelWeight = ele.pstyle('font-weight').strValue;
29278 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29279 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29280 var color = ele.pstyle('color').value;
29281 var outlineColor = ele.pstyle('text-outline-color').value;
29282 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29283 context.lineJoin = 'round'; // so text outlines aren't jagged
29284
29285 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29286 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29287}; // TODO ensure re-used
29288
29289
29290function roundRect(ctx, x, y, width, height) {
29291 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29292 ctx.beginPath();
29293 ctx.moveTo(x + radius, y);
29294 ctx.lineTo(x + width - radius, y);
29295 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29296 ctx.lineTo(x + width, y + height - radius);
29297 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29298 ctx.lineTo(x + radius, y + height);
29299 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29300 ctx.lineTo(x, y + radius);
29301 ctx.quadraticCurveTo(x, y, x + radius, y);
29302 ctx.closePath();
29303 ctx.fill();
29304}
29305
29306CRp$4.getTextAngle = function (ele, prefix) {
29307 var theta;
29308 var _p = ele._private;
29309 var rscratch = _p.rscratch;
29310 var pdash = prefix ? prefix + '-' : '';
29311 var rotation = ele.pstyle(pdash + 'text-rotation');
29312 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29313
29314 if (rotation.strValue === 'autorotate') {
29315 theta = ele.isEdge() ? textAngle : 0;
29316 } else if (rotation.strValue === 'none') {
29317 theta = 0;
29318 } else {
29319 theta = rotation.pfValue;
29320 }
29321
29322 return theta;
29323};
29324
29325CRp$4.drawText = function (context, ele, prefix) {
29326 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29327 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29328 var _p = ele._private;
29329 var rscratch = _p.rscratch;
29330 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29331
29332 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29333 return;
29334 } // use 'main' as an alias for the main label (i.e. null prefix)
29335
29336
29337 if (prefix === 'main') {
29338 prefix = null;
29339 }
29340
29341 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29342 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29343 var orgTextX, orgTextY; // used for rotation
29344
29345 var text = this.getLabelText(ele, prefix);
29346
29347 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29348 this.setupTextStyle(context, ele, useEleOpacity);
29349 var pdash = prefix ? prefix + '-' : '';
29350 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29351 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29352 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29353 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29354 var isEdge = ele.isEdge();
29355 var halign = ele.pstyle('text-halign').value;
29356 var valign = ele.pstyle('text-valign').value;
29357
29358 if (isEdge) {
29359 halign = 'center';
29360 valign = 'center';
29361 }
29362
29363 textX += marginX;
29364 textY += marginY;
29365 var theta;
29366
29367 if (!applyRotation) {
29368 theta = 0;
29369 } else {
29370 theta = this.getTextAngle(ele, prefix);
29371 }
29372
29373 if (theta !== 0) {
29374 orgTextX = textX;
29375 orgTextY = textY;
29376 context.translate(orgTextX, orgTextY);
29377 context.rotate(theta);
29378 textX = 0;
29379 textY = 0;
29380 }
29381
29382 switch (valign) {
29383 case 'top':
29384 break;
29385
29386 case 'center':
29387 textY += textH / 2;
29388 break;
29389
29390 case 'bottom':
29391 textY += textH;
29392 break;
29393 }
29394
29395 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29396 var borderOpacity = ele.pstyle('text-border-opacity').value;
29397 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29398 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29399
29400 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29401 var bgX = textX - backgroundPadding;
29402
29403 switch (halign) {
29404 case 'left':
29405 bgX -= textW;
29406 break;
29407
29408 case 'center':
29409 bgX -= textW / 2;
29410 break;
29411 }
29412
29413 var bgY = textY - textH - backgroundPadding;
29414 var bgW = textW + 2 * backgroundPadding;
29415 var bgH = textH + 2 * backgroundPadding;
29416
29417 if (backgroundOpacity > 0) {
29418 var textFill = context.fillStyle;
29419 var textBackgroundColor = ele.pstyle('text-background-color').value;
29420 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29421 var styleShape = ele.pstyle('text-background-shape').strValue;
29422
29423 if (styleShape.indexOf('round') === 0) {
29424 roundRect(context, bgX, bgY, bgW, bgH, 2);
29425 } else {
29426 context.fillRect(bgX, bgY, bgW, bgH);
29427 }
29428
29429 context.fillStyle = textFill;
29430 }
29431
29432 if (textBorderWidth > 0 && borderOpacity > 0) {
29433 var textStroke = context.strokeStyle;
29434 var textLineWidth = context.lineWidth;
29435 var textBorderColor = ele.pstyle('text-border-color').value;
29436 var textBorderStyle = ele.pstyle('text-border-style').value;
29437 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29438 context.lineWidth = textBorderWidth;
29439
29440 if (context.setLineDash) {
29441 // for very outofdate browsers
29442 switch (textBorderStyle) {
29443 case 'dotted':
29444 context.setLineDash([1, 1]);
29445 break;
29446
29447 case 'dashed':
29448 context.setLineDash([4, 2]);
29449 break;
29450
29451 case 'double':
29452 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29453
29454 context.setLineDash([]);
29455 break;
29456
29457 case 'solid':
29458 context.setLineDash([]);
29459 break;
29460 }
29461 }
29462
29463 context.strokeRect(bgX, bgY, bgW, bgH);
29464
29465 if (textBorderStyle === 'double') {
29466 var whiteWidth = textBorderWidth / 2;
29467 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29468 }
29469
29470 if (context.setLineDash) {
29471 // for very outofdate browsers
29472 context.setLineDash([]);
29473 }
29474
29475 context.lineWidth = textLineWidth;
29476 context.strokeStyle = textStroke;
29477 }
29478 }
29479
29480 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29481
29482 if (lineWidth > 0) {
29483 context.lineWidth = lineWidth;
29484 }
29485
29486 if (ele.pstyle('text-wrap').value === 'wrap') {
29487 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29488 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29489 var halfTextW = textW / 2;
29490 var justification = this.getLabelJustification(ele);
29491
29492 if (justification === 'auto') ; else if (halign === 'left') {
29493 // auto justification : right
29494 if (justification === 'left') {
29495 textX += -textW;
29496 } else if (justification === 'center') {
29497 textX += -halfTextW;
29498 } // else same as auto
29499
29500 } else if (halign === 'center') {
29501 // auto justfication : center
29502 if (justification === 'left') {
29503 textX += -halfTextW;
29504 } else if (justification === 'right') {
29505 textX += halfTextW;
29506 } // else same as auto
29507
29508 } else if (halign === 'right') {
29509 // auto justification : left
29510 if (justification === 'center') {
29511 textX += halfTextW;
29512 } else if (justification === 'right') {
29513 textX += textW;
29514 } // else same as auto
29515
29516 }
29517
29518 switch (valign) {
29519 case 'top':
29520 textY -= (lines.length - 1) * lineHeight;
29521 break;
29522
29523 case 'center':
29524 case 'bottom':
29525 textY -= (lines.length - 1) * lineHeight;
29526 break;
29527 }
29528
29529 for (var l = 0; l < lines.length; l++) {
29530 if (lineWidth > 0) {
29531 context.strokeText(lines[l], textX, textY);
29532 }
29533
29534 context.fillText(lines[l], textX, textY);
29535 textY += lineHeight;
29536 }
29537 } else {
29538 if (lineWidth > 0) {
29539 context.strokeText(text, textX, textY);
29540 }
29541
29542 context.fillText(text, textX, textY);
29543 }
29544
29545 if (theta !== 0) {
29546 context.rotate(-theta);
29547 context.translate(-orgTextX, -orgTextY);
29548 }
29549 }
29550};
29551
29552/* global Path2D */
29553var CRp$5 = {};
29554
29555CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29556 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29557 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29558 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29559 var r = this;
29560 var nodeWidth, nodeHeight;
29561 var _p = node._private;
29562 var rs = _p.rscratch;
29563 var pos = node.position();
29564
29565 if (!number(pos.x) || !number(pos.y)) {
29566 return; // can't draw node with undefined position
29567 }
29568
29569 if (shouldDrawOpacity && !node.visible()) {
29570 return;
29571 }
29572
29573 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29574 var usePaths = r.usePaths();
29575 var path;
29576 var pathCacheHit = false;
29577 var padding = node.padding();
29578 nodeWidth = node.width() + 2 * padding;
29579 nodeHeight = node.height() + 2 * padding; //
29580 // setup shift
29581
29582 var bb;
29583
29584 if (shiftToOriginWithBb) {
29585 bb = shiftToOriginWithBb;
29586 context.translate(-bb.x1, -bb.y1);
29587 } //
29588 // load bg image
29589
29590
29591 var bgImgProp = node.pstyle('background-image');
29592 var urls = bgImgProp.value;
29593 var urlDefined = new Array(urls.length);
29594 var image = new Array(urls.length);
29595 var numImages = 0;
29596
29597 for (var i = 0; i < urls.length; i++) {
29598 var url = urls[i];
29599 var defd = urlDefined[i] = url != null && url !== 'none';
29600
29601 if (defd) {
29602 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29603 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29604
29605 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29606 _p.backgroundTimestamp = Date.now();
29607 node.emitAndNotify('background');
29608 });
29609 }
29610 } //
29611 // setup styles
29612
29613
29614 var darkness = node.pstyle('background-blacken').value;
29615 var borderWidth = node.pstyle('border-width').pfValue;
29616 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29617 var borderColor = node.pstyle('border-color').value;
29618 var borderStyle = node.pstyle('border-style').value;
29619 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29620 context.lineJoin = 'miter'; // so borders are square with the node shape
29621
29622 var setupShapeColor = function setupShapeColor() {
29623 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29624 r.eleFillStyle(context, node, bgOpy);
29625 };
29626
29627 var setupBorderColor = function setupBorderColor() {
29628 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29629 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29630 }; //
29631 // setup shape
29632
29633
29634 var styleShape = node.pstyle('shape').strValue;
29635 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29636
29637 if (usePaths) {
29638 context.translate(pos.x, pos.y);
29639 var pathCache = r.nodePathCache = r.nodePathCache || [];
29640 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29641 var cachedPath = pathCache[key];
29642
29643 if (cachedPath != null) {
29644 path = cachedPath;
29645 pathCacheHit = true;
29646 rs.pathCache = path;
29647 } else {
29648 path = new Path2D();
29649 pathCache[key] = rs.pathCache = path;
29650 }
29651 }
29652
29653 var drawShape = function drawShape() {
29654 if (!pathCacheHit) {
29655 var npos = pos;
29656
29657 if (usePaths) {
29658 npos = {
29659 x: 0,
29660 y: 0
29661 };
29662 }
29663
29664 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29665 }
29666
29667 if (usePaths) {
29668 context.fill(path);
29669 } else {
29670 context.fill();
29671 }
29672 };
29673
29674 var drawImages = function drawImages() {
29675 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29676 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
29677 var prevBging = _p.backgrounding;
29678 var totalCompleted = 0;
29679
29680 for (var _i = 0; _i < image.length; _i++) {
29681 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
29682
29683 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
29684 totalCompleted++;
29685 continue;
29686 }
29687
29688 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29689 totalCompleted++;
29690 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29691 }
29692 }
29693
29694 _p.backgrounding = !(totalCompleted === numImages);
29695
29696 if (prevBging !== _p.backgrounding) {
29697 // update style b/c :backgrounding state changed
29698 node.updateStyle(false);
29699 }
29700 };
29701
29702 var drawPie = function drawPie() {
29703 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29704 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29705
29706 if (r.hasPie(node)) {
29707 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29708
29709 if (redrawShape) {
29710 if (!usePaths) {
29711 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29712 }
29713 }
29714 }
29715 };
29716
29717 var darken = function darken() {
29718 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29719 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29720 var c = darkness > 0 ? 0 : 255;
29721
29722 if (darkness !== 0) {
29723 r.colorFillStyle(context, c, c, c, opacity);
29724
29725 if (usePaths) {
29726 context.fill(path);
29727 } else {
29728 context.fill();
29729 }
29730 }
29731 };
29732
29733 var drawBorder = function drawBorder() {
29734 if (borderWidth > 0) {
29735 context.lineWidth = borderWidth;
29736 context.lineCap = 'butt';
29737
29738 if (context.setLineDash) {
29739 // for very outofdate browsers
29740 switch (borderStyle) {
29741 case 'dotted':
29742 context.setLineDash([1, 1]);
29743 break;
29744
29745 case 'dashed':
29746 context.setLineDash([4, 2]);
29747 break;
29748
29749 case 'solid':
29750 case 'double':
29751 context.setLineDash([]);
29752 break;
29753 }
29754 }
29755
29756 if (usePaths) {
29757 context.stroke(path);
29758 } else {
29759 context.stroke();
29760 }
29761
29762 if (borderStyle === 'double') {
29763 context.lineWidth = borderWidth / 3;
29764 var gco = context.globalCompositeOperation;
29765 context.globalCompositeOperation = 'destination-out';
29766
29767 if (usePaths) {
29768 context.stroke(path);
29769 } else {
29770 context.stroke();
29771 }
29772
29773 context.globalCompositeOperation = gco;
29774 } // reset in case we changed the border style
29775
29776
29777 if (context.setLineDash) {
29778 // for very outofdate browsers
29779 context.setLineDash([]);
29780 }
29781 }
29782 };
29783
29784 var drawOverlay = function drawOverlay() {
29785 if (shouldDrawOverlay) {
29786 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
29787 }
29788 };
29789
29790 var drawText = function drawText() {
29791 r.drawElementText(context, node, null, drawLabel);
29792 };
29793
29794 var ghost = node.pstyle('ghost').value === 'yes';
29795
29796 if (ghost) {
29797 var gx = node.pstyle('ghost-offset-x').pfValue;
29798 var gy = node.pstyle('ghost-offset-y').pfValue;
29799 var ghostOpacity = node.pstyle('ghost-opacity').value;
29800 var effGhostOpacity = ghostOpacity * eleOpacity;
29801 context.translate(gx, gy);
29802 setupShapeColor(ghostOpacity * bgOpacity);
29803 drawShape();
29804 drawImages(effGhostOpacity, true);
29805 setupBorderColor(ghostOpacity * borderOpacity);
29806 drawBorder();
29807 drawPie(darkness !== 0 || borderWidth !== 0);
29808 drawImages(effGhostOpacity, false);
29809 darken(effGhostOpacity);
29810 context.translate(-gx, -gy);
29811 }
29812
29813 setupShapeColor();
29814 drawShape();
29815 drawImages(eleOpacity, true);
29816 setupBorderColor();
29817 drawBorder();
29818 drawPie(darkness !== 0 || borderWidth !== 0);
29819 drawImages(eleOpacity, false);
29820 darken();
29821
29822 if (usePaths) {
29823 context.translate(-pos.x, -pos.y);
29824 }
29825
29826 drawText();
29827 drawOverlay(); //
29828 // clean up shift
29829
29830 if (shiftToOriginWithBb) {
29831 context.translate(bb.x1, bb.y1);
29832 }
29833};
29834
29835CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
29836 var r = this;
29837
29838 if (!node.visible()) {
29839 return;
29840 }
29841
29842 var overlayPadding = node.pstyle('overlay-padding').pfValue;
29843 var overlayOpacity = node.pstyle('overlay-opacity').value;
29844 var overlayColor = node.pstyle('overlay-color').value;
29845
29846 if (overlayOpacity > 0) {
29847 pos = pos || node.position();
29848
29849 if (nodeWidth == null || nodeHeight == null) {
29850 var padding = node.padding();
29851 nodeWidth = node.width() + 2 * padding;
29852 nodeHeight = node.height() + 2 * padding;
29853 }
29854
29855 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29856 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
29857 context.fill();
29858 }
29859}; // does the node have at least one pie piece?
29860
29861
29862CRp$5.hasPie = function (node) {
29863 node = node[0]; // ensure ele ref
29864
29865 return node._private.hasPie;
29866};
29867
29868CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
29869 node = node[0]; // ensure ele ref
29870
29871 pos = pos || node.position();
29872 var cyStyle = node.cy().style();
29873 var pieSize = node.pstyle('pie-size');
29874 var x = pos.x;
29875 var y = pos.y;
29876 var nodeW = node.width();
29877 var nodeH = node.height();
29878 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
29879
29880 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
29881
29882 var usePaths = this.usePaths();
29883
29884 if (usePaths) {
29885 x = 0;
29886 y = 0;
29887 }
29888
29889 if (pieSize.units === '%') {
29890 radius = radius * pieSize.pfValue;
29891 } else if (pieSize.pfValue !== undefined) {
29892 radius = pieSize.pfValue / 2;
29893 }
29894
29895 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
29896 // 1..N
29897 var size = node.pstyle('pie-' + i + '-background-size').value;
29898 var color = node.pstyle('pie-' + i + '-background-color').value;
29899 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
29900 var percent = size / 100; // map integer range [0, 100] to [0, 1]
29901 // percent can't push beyond 1
29902
29903 if (percent + lastPercent > 1) {
29904 percent = 1 - lastPercent;
29905 }
29906
29907 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
29908
29909 var angleDelta = 2 * Math.PI * percent;
29910 var angleEnd = angleStart + angleDelta; // ignore if
29911 // - zero size
29912 // - we're already beyond the full circle
29913 // - adding the current slice would go beyond the full circle
29914
29915 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
29916 continue;
29917 }
29918
29919 context.beginPath();
29920 context.moveTo(x, y);
29921 context.arc(x, y, radius, angleStart, angleEnd);
29922 context.closePath();
29923 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29924 context.fill();
29925 lastPercent += percent;
29926 }
29927};
29928
29929var CRp$6 = {};
29930var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
29931
29932CRp$6.getPixelRatio = function () {
29933 var context = this.data.contexts[0];
29934
29935 if (this.forcedPixelRatio != null) {
29936 return this.forcedPixelRatio;
29937 }
29938
29939 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
29940 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
29941};
29942
29943CRp$6.paintCache = function (context) {
29944 var caches = this.paintCaches = this.paintCaches || [];
29945 var needToCreateCache = true;
29946 var cache;
29947
29948 for (var i = 0; i < caches.length; i++) {
29949 cache = caches[i];
29950
29951 if (cache.context === context) {
29952 needToCreateCache = false;
29953 break;
29954 }
29955 }
29956
29957 if (needToCreateCache) {
29958 cache = {
29959 context: context
29960 };
29961 caches.push(cache);
29962 }
29963
29964 return cache;
29965};
29966
29967CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
29968 var gradientStyle;
29969 var usePaths = this.usePaths();
29970 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
29971 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
29972
29973 if (fill === 'radial-gradient') {
29974 if (ele.isEdge()) {
29975 var start = ele.sourceEndpoint(),
29976 end = ele.targetEndpoint(),
29977 mid = ele.midpoint();
29978 var d1 = dist(start, mid);
29979 var d2 = dist(end, mid);
29980 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
29981 } else {
29982 var pos = usePaths ? {
29983 x: 0,
29984 y: 0
29985 } : ele.position(),
29986 width = ele.paddedWidth(),
29987 height = ele.paddedHeight();
29988 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
29989 }
29990 } else {
29991 if (ele.isEdge()) {
29992 var _start = ele.sourceEndpoint(),
29993 _end = ele.targetEndpoint();
29994
29995 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
29996 } else {
29997 var _pos = usePaths ? {
29998 x: 0,
29999 y: 0
30000 } : ele.position(),
30001 _width = ele.paddedWidth(),
30002 _height = ele.paddedHeight(),
30003 halfWidth = _width / 2,
30004 halfHeight = _height / 2;
30005
30006 var direction = ele.pstyle('background-gradient-direction').value;
30007
30008 switch (direction) {
30009 case 'to-bottom':
30010 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
30011 break;
30012
30013 case 'to-top':
30014 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
30015 break;
30016
30017 case 'to-left':
30018 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
30019 break;
30020
30021 case 'to-right':
30022 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
30023 break;
30024
30025 case 'to-bottom-right':
30026 case 'to-right-bottom':
30027 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
30028 break;
30029
30030 case 'to-top-right':
30031 case 'to-right-top':
30032 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
30033 break;
30034
30035 case 'to-bottom-left':
30036 case 'to-left-bottom':
30037 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
30038 break;
30039
30040 case 'to-top-left':
30041 case 'to-left-top':
30042 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
30043 break;
30044 }
30045 }
30046 }
30047
30048 if (!gradientStyle) return null; // invalid gradient style
30049
30050 var hasPositions = positions.length === colors.length;
30051 var length = colors.length;
30052
30053 for (var i = 0; i < length; i++) {
30054 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
30055 }
30056
30057 return gradientStyle;
30058};
30059
30060CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
30061 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30062 if (!gradientStyle) return null; // error
30063
30064 context.fillStyle = gradientStyle;
30065};
30066
30067CRp$6.colorFillStyle = function (context, r, g, b, a) {
30068 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30069 // var cache = this.paintCache(context);
30070 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30071 // if( cache.fillStyle !== fillStyle ){
30072 // context.fillStyle = cache.fillStyle = fillStyle;
30073 // }
30074};
30075
30076CRp$6.eleFillStyle = function (context, ele, opacity) {
30077 var backgroundFill = ele.pstyle('background-fill').value;
30078
30079 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30080 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30081 } else {
30082 var backgroundColor = ele.pstyle('background-color').value;
30083 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30084 }
30085};
30086
30087CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
30088 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30089 if (!gradientStyle) return null; // error
30090
30091 context.strokeStyle = gradientStyle;
30092};
30093
30094CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
30095 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30096 // var cache = this.paintCache(context);
30097 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30098 // if( cache.strokeStyle !== strokeStyle ){
30099 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30100 // }
30101};
30102
30103CRp$6.eleStrokeStyle = function (context, ele, opacity) {
30104 var lineFill = ele.pstyle('line-fill').value;
30105
30106 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30107 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30108 } else {
30109 var lineColor = ele.pstyle('line-color').value;
30110 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30111 }
30112}; // Resize canvas
30113
30114
30115CRp$6.matchCanvasSize = function (container) {
30116 var r = this;
30117 var data = r.data;
30118 var bb = r.findContainerClientCoords();
30119 var width = bb[2];
30120 var height = bb[3];
30121 var pixelRatio = r.getPixelRatio();
30122 var mbPxRatio = r.motionBlurPxRatio;
30123
30124 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30125 pixelRatio = mbPxRatio;
30126 }
30127
30128 var canvasWidth = width * pixelRatio;
30129 var canvasHeight = height * pixelRatio;
30130 var canvas;
30131
30132 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30133 return; // save cycles if same
30134 }
30135
30136 r.fontCaches = null; // resizing resets the style
30137
30138 var canvasContainer = data.canvasContainer;
30139 canvasContainer.style.width = width + 'px';
30140 canvasContainer.style.height = height + 'px';
30141
30142 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30143 canvas = data.canvases[i];
30144 canvas.width = canvasWidth;
30145 canvas.height = canvasHeight;
30146 canvas.style.width = width + 'px';
30147 canvas.style.height = height + 'px';
30148 }
30149
30150 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30151 canvas = data.bufferCanvases[i];
30152 canvas.width = canvasWidth;
30153 canvas.height = canvasHeight;
30154 canvas.style.width = width + 'px';
30155 canvas.style.height = height + 'px';
30156 }
30157
30158 r.textureMult = 1;
30159
30160 if (pixelRatio <= 1) {
30161 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30162 r.textureMult = 2;
30163 canvas.width = canvasWidth * r.textureMult;
30164 canvas.height = canvasHeight * r.textureMult;
30165 }
30166
30167 r.canvasWidth = canvasWidth;
30168 r.canvasHeight = canvasHeight;
30169};
30170
30171CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30172 this.render({
30173 forcedContext: cxt,
30174 forcedZoom: zoom,
30175 forcedPan: pan,
30176 drawAllLayers: true,
30177 forcedPxRatio: pxRatio
30178 });
30179};
30180
30181CRp$6.render = function (options) {
30182 options = options || staticEmptyObject();
30183 var forcedContext = options.forcedContext;
30184 var drawAllLayers = options.drawAllLayers;
30185 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30186 var forcedZoom = options.forcedZoom;
30187 var forcedPan = options.forcedPan;
30188 var r = this;
30189 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30190 var cy = r.cy;
30191 var data = r.data;
30192 var needDraw = data.canvasNeedsRedraw;
30193 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30194 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30195 var mbPxRatio = r.motionBlurPxRatio;
30196 var hasCompoundNodes = cy.hasCompoundNodes();
30197 var inNodeDragGesture = r.hoverData.draggingEles;
30198 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30199 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30200 var motionBlurFadeEffect = motionBlur;
30201
30202 if (!forcedContext) {
30203 if (r.prevPxRatio !== pixelRatio) {
30204 r.invalidateContainerClientCoordsCache();
30205 r.matchCanvasSize(r.container);
30206 r.redrawHint('eles', true);
30207 r.redrawHint('drag', true);
30208 }
30209
30210 r.prevPxRatio = pixelRatio;
30211 }
30212
30213 if (!forcedContext && r.motionBlurTimeout) {
30214 clearTimeout(r.motionBlurTimeout);
30215 }
30216
30217 if (motionBlur) {
30218 if (r.mbFrames == null) {
30219 r.mbFrames = 0;
30220 }
30221
30222 r.mbFrames++;
30223
30224 if (r.mbFrames < 3) {
30225 // need several frames before even high quality motionblur
30226 motionBlurFadeEffect = false;
30227 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30228
30229
30230 if (r.mbFrames > r.minMbLowQualFrames) {
30231 //r.fullQualityMb = false;
30232 r.motionBlurPxRatio = r.mbPxRBlurry;
30233 }
30234 }
30235
30236 if (r.clearingMotionBlur) {
30237 r.motionBlurPxRatio = 1;
30238 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30239 // because a rogue async texture frame would clear needDraw
30240
30241
30242 if (r.textureDrawLastFrame && !textureDraw) {
30243 needDraw[r.NODE] = true;
30244 needDraw[r.SELECT_BOX] = true;
30245 }
30246
30247 var style = cy.style();
30248 var zoom = cy.zoom();
30249 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30250 var pan = cy.pan();
30251 var effectivePan = {
30252 x: pan.x,
30253 y: pan.y
30254 };
30255 var vp = {
30256 zoom: zoom,
30257 pan: {
30258 x: pan.x,
30259 y: pan.y
30260 }
30261 };
30262 var prevVp = r.prevViewport;
30263 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)
30264
30265 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30266 r.motionBlurPxRatio = 1;
30267 }
30268
30269 if (forcedPan) {
30270 effectivePan = forcedPan;
30271 } // apply pixel ratio
30272
30273
30274 effectiveZoom *= pixelRatio;
30275 effectivePan.x *= pixelRatio;
30276 effectivePan.y *= pixelRatio;
30277 var eles = r.getCachedZSortedEles();
30278
30279 function mbclear(context, x, y, w, h) {
30280 var gco = context.globalCompositeOperation;
30281 context.globalCompositeOperation = 'destination-out';
30282 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30283 context.fillRect(x, y, w, h);
30284 context.globalCompositeOperation = gco;
30285 }
30286
30287 function setContextTransform(context, clear) {
30288 var ePan, eZoom, w, h;
30289
30290 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30291 ePan = {
30292 x: pan.x * mbPxRatio,
30293 y: pan.y * mbPxRatio
30294 };
30295 eZoom = zoom * mbPxRatio;
30296 w = r.canvasWidth * mbPxRatio;
30297 h = r.canvasHeight * mbPxRatio;
30298 } else {
30299 ePan = effectivePan;
30300 eZoom = effectiveZoom;
30301 w = r.canvasWidth;
30302 h = r.canvasHeight;
30303 }
30304
30305 context.setTransform(1, 0, 0, 1, 0, 0);
30306
30307 if (clear === 'motionBlur') {
30308 mbclear(context, 0, 0, w, h);
30309 } else if (!forcedContext && (clear === undefined || clear)) {
30310 context.clearRect(0, 0, w, h);
30311 }
30312
30313 if (!drawAllLayers) {
30314 context.translate(ePan.x, ePan.y);
30315 context.scale(eZoom, eZoom);
30316 }
30317
30318 if (forcedPan) {
30319 context.translate(forcedPan.x, forcedPan.y);
30320 }
30321
30322 if (forcedZoom) {
30323 context.scale(forcedZoom, forcedZoom);
30324 }
30325 }
30326
30327 if (!textureDraw) {
30328 r.textureDrawLastFrame = false;
30329 }
30330
30331 if (textureDraw) {
30332 r.textureDrawLastFrame = true;
30333
30334 if (!r.textureCache) {
30335 r.textureCache = {};
30336 r.textureCache.bb = cy.mutableElements().boundingBox();
30337 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30338 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30339 cxt.setTransform(1, 0, 0, 1, 0, 0);
30340 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30341 r.render({
30342 forcedContext: cxt,
30343 drawOnlyNodeLayer: true,
30344 forcedPxRatio: pixelRatio * r.textureMult
30345 });
30346 var vp = r.textureCache.viewport = {
30347 zoom: cy.zoom(),
30348 pan: cy.pan(),
30349 width: r.canvasWidth,
30350 height: r.canvasHeight
30351 };
30352 vp.mpan = {
30353 x: (0 - vp.pan.x) / vp.zoom,
30354 y: (0 - vp.pan.y) / vp.zoom
30355 };
30356 }
30357
30358 needDraw[r.DRAG] = false;
30359 needDraw[r.NODE] = false;
30360 var context = data.contexts[r.NODE];
30361 var texture = r.textureCache.texture;
30362 var vp = r.textureCache.viewport;
30363 context.setTransform(1, 0, 0, 1, 0, 0);
30364
30365 if (motionBlur) {
30366 mbclear(context, 0, 0, vp.width, vp.height);
30367 } else {
30368 context.clearRect(0, 0, vp.width, vp.height);
30369 }
30370
30371 var outsideBgColor = style.core('outside-texture-bg-color').value;
30372 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30373 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30374 context.fillRect(0, 0, vp.width, vp.height);
30375 var zoom = cy.zoom();
30376 setContextTransform(context, false);
30377 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30378 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30379 } else if (r.textureOnViewport && !forcedContext) {
30380 // clear the cache since we don't need it
30381 r.textureCache = null;
30382 }
30383
30384 var extent = cy.extent();
30385 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30386 var hideEdges = r.hideEdgesOnViewport && vpManip;
30387 var needMbClear = [];
30388 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30389
30390 if (needMbClear[r.NODE]) {
30391 r.clearedForMotionBlur[r.NODE] = true;
30392 }
30393
30394 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30395
30396 if (needMbClear[r.DRAG]) {
30397 r.clearedForMotionBlur[r.DRAG] = true;
30398 }
30399
30400 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30401 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30402 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30403 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30404 setContextTransform(context, clear);
30405
30406 if (hideEdges) {
30407 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30408 } else {
30409 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30410 }
30411
30412 if (r.debug) {
30413 r.drawDebugPoints(context, eles.nondrag);
30414 }
30415
30416 if (!drawAllLayers && !motionBlur) {
30417 needDraw[r.NODE] = false;
30418 }
30419 }
30420
30421 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30422 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30423 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30424 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30425
30426 if (hideEdges) {
30427 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30428 } else {
30429 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30430 }
30431
30432 if (r.debug) {
30433 r.drawDebugPoints(context, eles.drag);
30434 }
30435
30436 if (!drawAllLayers && !motionBlur) {
30437 needDraw[r.DRAG] = false;
30438 }
30439 }
30440
30441 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30442 var context = forcedContext || data.contexts[r.SELECT_BOX];
30443 setContextTransform(context);
30444
30445 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30446 var zoom = r.cy.zoom();
30447 var borderWidth = style.core('selection-box-border-width').value / zoom;
30448 context.lineWidth = borderWidth;
30449 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 + ')';
30450 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30451
30452 if (borderWidth > 0) {
30453 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 + ')';
30454 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30455 }
30456 }
30457
30458 if (data.bgActivePosistion && !r.hoverData.selecting) {
30459 var zoom = r.cy.zoom();
30460 var pos = data.bgActivePosistion;
30461 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 + ')';
30462 context.beginPath();
30463 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30464 context.fill();
30465 }
30466
30467 var timeToRender = r.lastRedrawTime;
30468
30469 if (r.showFps && timeToRender) {
30470 timeToRender = Math.round(timeToRender);
30471 var fps = Math.round(1000 / timeToRender);
30472 context.setTransform(1, 0, 0, 1, 0, 0);
30473 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30474 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30475 context.lineWidth = 1;
30476 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30477 var maxFps = 60;
30478 context.strokeRect(0, 30, 250, 20);
30479 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30480 }
30481
30482 if (!drawAllLayers) {
30483 needDraw[r.SELECT_BOX] = false;
30484 }
30485 } // motionblur: blit rendered blurry frames
30486
30487
30488 if (motionBlur && mbPxRatio !== 1) {
30489 var cxtNode = data.contexts[r.NODE];
30490 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30491 var cxtDrag = data.contexts[r.DRAG];
30492 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30493
30494 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30495 cxt.setTransform(1, 0, 0, 1, 0, 0);
30496
30497 if (needClear || !motionBlurFadeEffect) {
30498 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30499 } else {
30500 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30501 }
30502
30503 var pxr = mbPxRatio;
30504 cxt.drawImage(txt, // img
30505 0, 0, // sx, sy
30506 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30507 0, 0, // x, y
30508 r.canvasWidth, r.canvasHeight // w, h
30509 );
30510 };
30511
30512 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30513 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30514 needDraw[r.NODE] = false;
30515 }
30516
30517 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30518 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30519 needDraw[r.DRAG] = false;
30520 }
30521 }
30522
30523 r.prevViewport = vp;
30524
30525 if (r.clearingMotionBlur) {
30526 r.clearingMotionBlur = false;
30527 r.motionBlurCleared = true;
30528 r.motionBlur = true;
30529 }
30530
30531 if (motionBlur) {
30532 r.motionBlurTimeout = setTimeout(function () {
30533 r.motionBlurTimeout = null;
30534 r.clearedForMotionBlur[r.NODE] = false;
30535 r.clearedForMotionBlur[r.DRAG] = false;
30536 r.motionBlur = false;
30537 r.clearingMotionBlur = !textureDraw;
30538 r.mbFrames = 0;
30539 needDraw[r.NODE] = true;
30540 needDraw[r.DRAG] = true;
30541 r.redraw();
30542 }, motionBlurDelay);
30543 }
30544
30545 if (!forcedContext) {
30546 cy.emit('render');
30547 }
30548};
30549
30550var CRp$7 = {}; // @O Polygon drawing
30551
30552CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
30553 var halfW = width / 2;
30554 var halfH = height / 2;
30555
30556 if (context.beginPath) {
30557 context.beginPath();
30558 }
30559
30560 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30561
30562 for (var i = 1; i < points.length / 2; i++) {
30563 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30564 }
30565
30566 context.closePath();
30567};
30568
30569CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30570 var halfW = width / 2;
30571 var halfH = height / 2;
30572 var cornerRadius = getRoundPolygonRadius(width, height);
30573
30574 if (context.beginPath) {
30575 context.beginPath();
30576 }
30577
30578 for (var _i = 0; _i < points.length / 4; _i++) {
30579 var sourceUv = void 0,
30580 destUv = void 0;
30581
30582 if (_i === 0) {
30583 sourceUv = points.length - 2;
30584 } else {
30585 sourceUv = _i * 4 - 2;
30586 }
30587
30588 destUv = _i * 4 + 2;
30589 var px = x + halfW * points[_i * 4];
30590 var py = y + halfH * points[_i * 4 + 1];
30591 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30592 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30593 var cp0x = px - offset * points[sourceUv];
30594 var cp0y = py - offset * points[sourceUv + 1];
30595 var cp1x = px + offset * points[destUv];
30596 var cp1y = py + offset * points[destUv + 1];
30597
30598 if (_i === 0) {
30599 context.moveTo(cp0x, cp0y);
30600 } else {
30601 context.lineTo(cp0x, cp0y);
30602 }
30603
30604 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30605 }
30606
30607 context.closePath();
30608}; // Round rectangle drawing
30609
30610
30611CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
30612 var halfWidth = width / 2;
30613 var halfHeight = height / 2;
30614 var cornerRadius = getRoundRectangleRadius(width, height);
30615
30616 if (context.beginPath) {
30617 context.beginPath();
30618 } // Start at top middle
30619
30620
30621 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30622
30623 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30624
30625 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30626
30627 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30628
30629 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30630
30631 context.lineTo(x, y - halfHeight);
30632 context.closePath();
30633};
30634
30635CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30636 var halfWidth = width / 2;
30637 var halfHeight = height / 2;
30638 var cornerRadius = getRoundRectangleRadius(width, height);
30639
30640 if (context.beginPath) {
30641 context.beginPath();
30642 } // Start at top middle
30643
30644
30645 context.moveTo(x, y - halfHeight);
30646 context.lineTo(x + halfWidth, y - halfHeight);
30647 context.lineTo(x + halfWidth, y);
30648 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30649 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30650 context.lineTo(x - halfWidth, y - halfHeight);
30651 context.lineTo(x, y - halfHeight);
30652 context.closePath();
30653};
30654
30655CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
30656 var halfWidth = width / 2;
30657 var halfHeight = height / 2;
30658 var cornerLength = getCutRectangleCornerLength();
30659
30660 if (context.beginPath) {
30661 context.beginPath();
30662 }
30663
30664 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30665 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30666 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30667 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30668 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30669 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30670 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30671 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30672 context.closePath();
30673};
30674
30675CRp$7.drawBarrelPath = function (context, x, y, width, height) {
30676 var halfWidth = width / 2;
30677 var halfHeight = height / 2;
30678 var xBegin = x - halfWidth;
30679 var xEnd = x + halfWidth;
30680 var yBegin = y - halfHeight;
30681 var yEnd = y + halfHeight;
30682 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30683 var wOffset = barrelCurveConstants.widthOffset;
30684 var hOffset = barrelCurveConstants.heightOffset;
30685 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30686
30687 if (context.beginPath) {
30688 context.beginPath();
30689 }
30690
30691 context.moveTo(xBegin, yBegin + hOffset);
30692 context.lineTo(xBegin, yEnd - hOffset);
30693 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30694 context.lineTo(xEnd - wOffset, yEnd);
30695 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30696 context.lineTo(xEnd, yBegin + hOffset);
30697 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30698 context.lineTo(xBegin + wOffset, yBegin);
30699 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30700 context.closePath();
30701};
30702
30703var sin0 = Math.sin(0);
30704var cos0 = Math.cos(0);
30705var sin = {};
30706var cos = {};
30707var ellipseStepSize = Math.PI / 40;
30708
30709for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30710 sin[i] = Math.sin(i);
30711 cos[i] = Math.cos(i);
30712}
30713
30714CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
30715 if (context.beginPath) {
30716 context.beginPath();
30717 }
30718
30719 if (context.ellipse) {
30720 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30721 } else {
30722 var xPos, yPos;
30723 var rw = width / 2;
30724 var rh = height / 2;
30725
30726 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30727 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30728 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30729
30730 if (i === 0) {
30731 context.moveTo(xPos, yPos);
30732 } else {
30733 context.lineTo(xPos, yPos);
30734 }
30735 }
30736 }
30737
30738 context.closePath();
30739};
30740
30741/* global atob, ArrayBuffer, Uint8Array, Blob */
30742var CRp$8 = {};
30743
30744CRp$8.createBuffer = function (w, h) {
30745 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30746
30747 buffer.width = w;
30748 buffer.height = h;
30749 return [buffer, buffer.getContext('2d')];
30750};
30751
30752CRp$8.bufferCanvasImage = function (options) {
30753 var cy = this.cy;
30754 var eles = cy.mutableElements();
30755 var bb = eles.boundingBox();
30756 var ctrRect = this.findContainerClientCoords();
30757 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
30758 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
30759 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
30760 var pxRatio = this.getPixelRatio();
30761 var scale = 1;
30762
30763 if (options.scale !== undefined) {
30764 width *= options.scale;
30765 height *= options.scale;
30766 scale = options.scale;
30767 } else if (specdMaxDims) {
30768 var maxScaleW = Infinity;
30769 var maxScaleH = Infinity;
30770
30771 if (number(options.maxWidth)) {
30772 maxScaleW = scale * options.maxWidth / width;
30773 }
30774
30775 if (number(options.maxHeight)) {
30776 maxScaleH = scale * options.maxHeight / height;
30777 }
30778
30779 scale = Math.min(maxScaleW, maxScaleH);
30780 width *= scale;
30781 height *= scale;
30782 }
30783
30784 if (!specdMaxDims) {
30785 width *= pxRatio;
30786 height *= pxRatio;
30787 scale *= pxRatio;
30788 }
30789
30790 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
30791
30792 buffCanvas.width = width;
30793 buffCanvas.height = height;
30794 buffCanvas.style.width = width + 'px';
30795 buffCanvas.style.height = height + 'px';
30796 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
30797
30798 if (width > 0 && height > 0) {
30799 buffCxt.clearRect(0, 0, width, height);
30800 buffCxt.globalCompositeOperation = 'source-over';
30801 var zsortedEles = this.getCachedZSortedEles();
30802
30803 if (options.full) {
30804 // draw the full bounds of the graph
30805 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
30806 buffCxt.scale(scale, scale);
30807 this.drawElements(buffCxt, zsortedEles);
30808 buffCxt.scale(1 / scale, 1 / scale);
30809 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
30810 } else {
30811 // draw the current view
30812 var pan = cy.pan();
30813 var translation = {
30814 x: pan.x * scale,
30815 y: pan.y * scale
30816 };
30817 scale *= cy.zoom();
30818 buffCxt.translate(translation.x, translation.y);
30819 buffCxt.scale(scale, scale);
30820 this.drawElements(buffCxt, zsortedEles);
30821 buffCxt.scale(1 / scale, 1 / scale);
30822 buffCxt.translate(-translation.x, -translation.y);
30823 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
30824
30825
30826 if (options.bg) {
30827 buffCxt.globalCompositeOperation = 'destination-over';
30828 buffCxt.fillStyle = options.bg;
30829 buffCxt.rect(0, 0, width, height);
30830 buffCxt.fill();
30831 }
30832 }
30833
30834 return buffCanvas;
30835};
30836
30837function b64ToBlob(b64, mimeType) {
30838 var bytes = atob(b64);
30839 var buff = new ArrayBuffer(bytes.length);
30840 var buffUint8 = new Uint8Array(buff);
30841
30842 for (var i = 0; i < bytes.length; i++) {
30843 buffUint8[i] = bytes.charCodeAt(i);
30844 }
30845
30846 return new Blob([buff], {
30847 type: mimeType
30848 });
30849}
30850
30851function b64UriToB64(b64uri) {
30852 var i = b64uri.indexOf(',');
30853 return b64uri.substr(i + 1);
30854}
30855
30856function output(options, canvas, mimeType) {
30857 var getB64Uri = function getB64Uri() {
30858 return canvas.toDataURL(mimeType, options.quality);
30859 };
30860
30861 switch (options.output) {
30862 case 'blob-promise':
30863 return new Promise$1(function (resolve, reject) {
30864 try {
30865 canvas.toBlob(function (blob) {
30866 if (blob != null) {
30867 resolve(blob);
30868 } else {
30869 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
30870 }
30871 }, mimeType, options.quality);
30872 } catch (err) {
30873 reject(err);
30874 }
30875 });
30876
30877 case 'blob':
30878 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
30879
30880 case 'base64':
30881 return b64UriToB64(getB64Uri());
30882
30883 case 'base64uri':
30884 default:
30885 return getB64Uri();
30886 }
30887}
30888
30889CRp$8.png = function (options) {
30890 return output(options, this.bufferCanvasImage(options), 'image/png');
30891};
30892
30893CRp$8.jpg = function (options) {
30894 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
30895};
30896
30897var CRp$9 = {};
30898
30899CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
30900 switch (name) {
30901 case 'ellipse':
30902 return this.drawEllipsePath(context, centerX, centerY, width, height);
30903
30904 case 'polygon':
30905 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
30906
30907 case 'round-polygon':
30908 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
30909
30910 case 'roundrectangle':
30911 case 'round-rectangle':
30912 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
30913
30914 case 'cutrectangle':
30915 case 'cut-rectangle':
30916 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
30917
30918 case 'bottomroundrectangle':
30919 case 'bottom-round-rectangle':
30920 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
30921
30922 case 'barrel':
30923 return this.drawBarrelPath(context, centerX, centerY, width, height);
30924 }
30925};
30926
30927var CR = CanvasRenderer;
30928var CRp$a = CanvasRenderer.prototype;
30929CRp$a.CANVAS_LAYERS = 3; //
30930
30931CRp$a.SELECT_BOX = 0;
30932CRp$a.DRAG = 1;
30933CRp$a.NODE = 2;
30934CRp$a.BUFFER_COUNT = 3; //
30935
30936CRp$a.TEXTURE_BUFFER = 0;
30937CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
30938CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
30939
30940function CanvasRenderer(options) {
30941 var r = this;
30942 r.data = {
30943 canvases: new Array(CRp$a.CANVAS_LAYERS),
30944 contexts: new Array(CRp$a.CANVAS_LAYERS),
30945 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
30946 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
30947 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
30948 };
30949 var tapHlOffAttr = '-webkit-tap-highlight-color';
30950 var tapHlOffStyle = 'rgba(0,0,0,0)';
30951 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
30952
30953 var containerStyle = r.data.canvasContainer.style;
30954 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
30955 containerStyle.position = 'relative';
30956 containerStyle.zIndex = '0';
30957 containerStyle.overflow = 'hidden';
30958 var container = options.cy.container();
30959 container.appendChild(r.data.canvasContainer);
30960 container.style[tapHlOffAttr] = tapHlOffStyle;
30961 var styleMap = {
30962 '-webkit-user-select': 'none',
30963 '-moz-user-select': '-moz-none',
30964 'user-select': 'none',
30965 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
30966 'outline-style': 'none'
30967 };
30968
30969 if (ms()) {
30970 styleMap['-ms-touch-action'] = 'none';
30971 styleMap['touch-action'] = 'none';
30972 }
30973
30974 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
30975 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30976
30977 r.data.contexts[i] = canvas.getContext('2d');
30978 Object.keys(styleMap).forEach(function (k) {
30979 canvas.style[k] = styleMap[k];
30980 });
30981 canvas.style.position = 'absolute';
30982 canvas.setAttribute('data-id', 'layer' + i);
30983 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
30984 r.data.canvasContainer.appendChild(canvas);
30985 r.data.canvasNeedsRedraw[i] = false;
30986 }
30987
30988 r.data.topCanvas = r.data.canvases[0];
30989 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
30990 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
30991 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
30992
30993 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
30994 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30995
30996 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
30997 r.data.bufferCanvases[i].style.position = 'absolute';
30998 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
30999 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
31000 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
31001 }
31002
31003 r.pathsEnabled = true;
31004 var emptyBb = makeBoundingBox();
31005
31006 var getBoxCenter = function getBoxCenter(bb) {
31007 return {
31008 x: (bb.x1 + bb.x2) / 2,
31009 y: (bb.y1 + bb.y2) / 2
31010 };
31011 };
31012
31013 var getCenterOffset = function getCenterOffset(bb) {
31014 return {
31015 x: -bb.w / 2,
31016 y: -bb.h / 2
31017 };
31018 };
31019
31020 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
31021 var _p = ele[0]._private;
31022 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
31023 return !same;
31024 };
31025
31026 var getStyleKey = function getStyleKey(ele) {
31027 return ele[0]._private.nodeKey;
31028 };
31029
31030 var getLabelKey = function getLabelKey(ele) {
31031 return ele[0]._private.labelStyleKey;
31032 };
31033
31034 var getSourceLabelKey = function getSourceLabelKey(ele) {
31035 return ele[0]._private.sourceLabelStyleKey;
31036 };
31037
31038 var getTargetLabelKey = function getTargetLabelKey(ele) {
31039 return ele[0]._private.targetLabelStyleKey;
31040 };
31041
31042 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
31043 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
31044 };
31045
31046 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31047 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
31048 };
31049
31050 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31051 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
31052 };
31053
31054 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31055 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
31056 };
31057
31058 var getElementBox = function getElementBox(ele) {
31059 ele.boundingBox();
31060 return ele[0]._private.bodyBounds;
31061 };
31062
31063 var getLabelBox = function getLabelBox(ele) {
31064 ele.boundingBox();
31065 return ele[0]._private.labelBounds.main || emptyBb;
31066 };
31067
31068 var getSourceLabelBox = function getSourceLabelBox(ele) {
31069 ele.boundingBox();
31070 return ele[0]._private.labelBounds.source || emptyBb;
31071 };
31072
31073 var getTargetLabelBox = function getTargetLabelBox(ele) {
31074 ele.boundingBox();
31075 return ele[0]._private.labelBounds.target || emptyBb;
31076 };
31077
31078 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31079 return scaledLabelShown;
31080 };
31081
31082 var getElementRotationPoint = function getElementRotationPoint(ele) {
31083 return getBoxCenter(getElementBox(ele));
31084 };
31085
31086 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31087 var pre = prefix ? prefix + '-' : '';
31088 return {
31089 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31090 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31091 };
31092 };
31093
31094 var getRsPt = function getRsPt(ele, x, y) {
31095 var rs = ele[0]._private.rscratch;
31096 return {
31097 x: rs[x],
31098 y: rs[y]
31099 };
31100 };
31101
31102 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31103 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31104 };
31105
31106 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31107 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31108 };
31109
31110 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31111 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31112 };
31113
31114 var getElementRotationOffset = function getElementRotationOffset(ele) {
31115 return getCenterOffset(getElementBox(ele));
31116 };
31117
31118 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31119 return getCenterOffset(getSourceLabelBox(ele));
31120 };
31121
31122 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31123 return getCenterOffset(getTargetLabelBox(ele));
31124 };
31125
31126 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31127 var bb = getLabelBox(ele);
31128 var p = getCenterOffset(getLabelBox(ele));
31129
31130 if (ele.isNode()) {
31131 switch (ele.pstyle('text-halign').value) {
31132 case 'left':
31133 p.x = -bb.w;
31134 break;
31135
31136 case 'right':
31137 p.x = 0;
31138 break;
31139 }
31140
31141 switch (ele.pstyle('text-valign').value) {
31142 case 'top':
31143 p.y = -bb.h;
31144 break;
31145
31146 case 'bottom':
31147 p.y = 0;
31148 break;
31149 }
31150 }
31151
31152 return p;
31153 };
31154
31155 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31156 getKey: getStyleKey,
31157 doesEleInvalidateKey: backgroundTimestampHasChanged,
31158 drawElement: drawElement,
31159 getBoundingBox: getElementBox,
31160 getRotationPoint: getElementRotationPoint,
31161 getRotationOffset: getElementRotationOffset,
31162 allowEdgeTxrCaching: false,
31163 allowParentTxrCaching: false
31164 });
31165 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31166 getKey: getLabelKey,
31167 drawElement: drawLabel,
31168 getBoundingBox: getLabelBox,
31169 getRotationPoint: getLabelRotationPoint,
31170 getRotationOffset: getLabelRotationOffset,
31171 isVisible: isLabelVisibleAtScale
31172 });
31173 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31174 getKey: getSourceLabelKey,
31175 drawElement: drawSourceLabel,
31176 getBoundingBox: getSourceLabelBox,
31177 getRotationPoint: getSourceLabelRotationPoint,
31178 getRotationOffset: getSourceLabelRotationOffset,
31179 isVisible: isLabelVisibleAtScale
31180 });
31181 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31182 getKey: getTargetLabelKey,
31183 drawElement: drawTargetLabel,
31184 getBoundingBox: getTargetLabelBox,
31185 getRotationPoint: getTargetLabelRotationPoint,
31186 getRotationOffset: getTargetLabelRotationOffset,
31187 isVisible: isLabelVisibleAtScale
31188 });
31189 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31190 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31191 // each cache should check for sub-key diff to see that the update affects that cache particularly
31192 eleTxrCache.invalidateElements(eles);
31193 lblTxrCache.invalidateElements(eles);
31194 slbTxrCache.invalidateElements(eles);
31195 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31196
31197 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31198
31199 for (var _i = 0; _i < eles.length; _i++) {
31200 var _p = eles[_i]._private;
31201 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31202 }
31203 });
31204
31205 var refineInLayers = function refineInLayers(reqs) {
31206 for (var i = 0; i < reqs.length; i++) {
31207 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31208 }
31209 };
31210
31211 eleTxrCache.onDequeue(refineInLayers);
31212 lblTxrCache.onDequeue(refineInLayers);
31213 slbTxrCache.onDequeue(refineInLayers);
31214 tlbTxrCache.onDequeue(refineInLayers);
31215}
31216
31217CRp$a.redrawHint = function (group, bool) {
31218 var r = this;
31219
31220 switch (group) {
31221 case 'eles':
31222 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31223 break;
31224
31225 case 'drag':
31226 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31227 break;
31228
31229 case 'select':
31230 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31231 break;
31232 }
31233}; // whether to use Path2D caching for drawing
31234
31235
31236var pathsImpld = typeof Path2D !== 'undefined';
31237
31238CRp$a.path2dEnabled = function (on) {
31239 if (on === undefined) {
31240 return this.pathsEnabled;
31241 }
31242
31243 this.pathsEnabled = on ? true : false;
31244};
31245
31246CRp$a.usePaths = function () {
31247 return pathsImpld && this.pathsEnabled;
31248};
31249
31250CRp$a.setImgSmoothing = function (context, bool) {
31251 if (context.imageSmoothingEnabled != null) {
31252 context.imageSmoothingEnabled = bool;
31253 } else {
31254 context.webkitImageSmoothingEnabled = bool;
31255 context.mozImageSmoothingEnabled = bool;
31256 context.msImageSmoothingEnabled = bool;
31257 }
31258};
31259
31260CRp$a.getImgSmoothing = function (context) {
31261 if (context.imageSmoothingEnabled != null) {
31262 return context.imageSmoothingEnabled;
31263 } else {
31264 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31265 }
31266};
31267
31268CRp$a.makeOffscreenCanvas = function (width, height) {
31269 var canvas;
31270
31271 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
31272 canvas = new OffscreenCanvas(width, height);
31273 } else {
31274 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31275
31276 canvas.width = width;
31277 canvas.height = height;
31278 }
31279
31280 return canvas;
31281};
31282
31283[CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
31284 extend(CRp$a, props);
31285});
31286
31287var renderer = [{
31288 name: 'null',
31289 impl: NullRenderer
31290}, {
31291 name: 'base',
31292 impl: BR
31293}, {
31294 name: 'canvas',
31295 impl: CR
31296}];
31297
31298var incExts = [{
31299 type: 'layout',
31300 extensions: layout
31301}, {
31302 type: 'renderer',
31303 extensions: renderer
31304}];
31305
31306var extensions = {}; // registered modules for extensions, indexed by name
31307
31308var modules = {};
31309
31310function setExtension(type, name, registrant) {
31311 var ext = registrant;
31312
31313 var overrideErr = function overrideErr(field) {
31314 warn('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31315 };
31316
31317 if (type === 'core') {
31318 if (Core.prototype[name]) {
31319 return overrideErr(name);
31320 } else {
31321 Core.prototype[name] = registrant;
31322 }
31323 } else if (type === 'collection') {
31324 if (Collection.prototype[name]) {
31325 return overrideErr(name);
31326 } else {
31327 Collection.prototype[name] = registrant;
31328 }
31329 } else if (type === 'layout') {
31330 // fill in missing layout functions in the prototype
31331 var Layout = function Layout(options) {
31332 this.options = options;
31333 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31334
31335 if (!plainObject(this._private)) {
31336 this._private = {};
31337 }
31338
31339 this._private.cy = options.cy;
31340 this._private.listeners = [];
31341 this.createEmitter();
31342 };
31343
31344 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31345 var optLayoutFns = [];
31346
31347 for (var i = 0; i < optLayoutFns.length; i++) {
31348 var fnName = optLayoutFns[i];
31349
31350 layoutProto[fnName] = layoutProto[fnName] || function () {
31351 return this;
31352 };
31353 } // either .start() or .run() is defined, so autogen the other
31354
31355
31356 if (layoutProto.start && !layoutProto.run) {
31357 layoutProto.run = function () {
31358 this.start();
31359 return this;
31360 };
31361 } else if (!layoutProto.start && layoutProto.run) {
31362 layoutProto.start = function () {
31363 this.run();
31364 return this;
31365 };
31366 }
31367
31368 var regStop = registrant.prototype.stop;
31369
31370 layoutProto.stop = function () {
31371 var opts = this.options;
31372
31373 if (opts && opts.animate) {
31374 var anis = this.animations;
31375
31376 if (anis) {
31377 for (var _i = 0; _i < anis.length; _i++) {
31378 anis[_i].stop();
31379 }
31380 }
31381 }
31382
31383 if (regStop) {
31384 regStop.call(this);
31385 } else {
31386 this.emit('layoutstop');
31387 }
31388
31389 return this;
31390 };
31391
31392 if (!layoutProto.destroy) {
31393 layoutProto.destroy = function () {
31394 return this;
31395 };
31396 }
31397
31398 layoutProto.cy = function () {
31399 return this._private.cy;
31400 };
31401
31402 var getCy = function getCy(layout) {
31403 return layout._private.cy;
31404 };
31405
31406 var emitterOpts = {
31407 addEventFields: function addEventFields(layout, evt) {
31408 evt.layout = layout;
31409 evt.cy = getCy(layout);
31410 evt.target = layout;
31411 },
31412 bubble: function bubble() {
31413 return true;
31414 },
31415 parent: function parent(layout) {
31416 return getCy(layout);
31417 }
31418 };
31419 extend(layoutProto, {
31420 createEmitter: function createEmitter() {
31421 this._private.emitter = new Emitter(emitterOpts, this);
31422 return this;
31423 },
31424 emitter: function emitter() {
31425 return this._private.emitter;
31426 },
31427 on: function on(evt, cb) {
31428 this.emitter().on(evt, cb);
31429 return this;
31430 },
31431 one: function one(evt, cb) {
31432 this.emitter().one(evt, cb);
31433 return this;
31434 },
31435 once: function once(evt, cb) {
31436 this.emitter().one(evt, cb);
31437 return this;
31438 },
31439 removeListener: function removeListener(evt, cb) {
31440 this.emitter().removeListener(evt, cb);
31441 return this;
31442 },
31443 removeAllListeners: function removeAllListeners() {
31444 this.emitter().removeAllListeners();
31445 return this;
31446 },
31447 emit: function emit(evt, params) {
31448 this.emitter().emit(evt, params);
31449 return this;
31450 }
31451 });
31452 define$3.eventAliasesOn(layoutProto);
31453 ext = Layout; // replace with our wrapped layout
31454 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31455 // user registered renderers inherit from base
31456 var BaseRenderer = getExtension('renderer', 'base');
31457 var bProto = BaseRenderer.prototype;
31458 var RegistrantRenderer = registrant;
31459 var rProto = registrant.prototype;
31460
31461 var Renderer = function Renderer() {
31462 BaseRenderer.apply(this, arguments);
31463 RegistrantRenderer.apply(this, arguments);
31464 };
31465
31466 var proto = Renderer.prototype;
31467
31468 for (var pName in bProto) {
31469 var pVal = bProto[pName];
31470 var existsInR = rProto[pName] != null;
31471
31472 if (existsInR) {
31473 return overrideErr(pName);
31474 }
31475
31476 proto[pName] = pVal; // take impl from base
31477 }
31478
31479 for (var _pName in rProto) {
31480 proto[_pName] = rProto[_pName]; // take impl from registrant
31481 }
31482
31483 bProto.clientFunctions.forEach(function (name) {
31484 proto[name] = proto[name] || function () {
31485 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31486 };
31487 });
31488 ext = Renderer;
31489 }
31490
31491 return setMap({
31492 map: extensions,
31493 keys: [type, name],
31494 value: ext
31495 });
31496}
31497
31498function getExtension(type, name) {
31499 return getMap({
31500 map: extensions,
31501 keys: [type, name]
31502 });
31503}
31504
31505function setModule(type, name, moduleType, moduleName, registrant) {
31506 return setMap({
31507 map: modules,
31508 keys: [type, name, moduleType, moduleName],
31509 value: registrant
31510 });
31511}
31512
31513function getModule(type, name, moduleType, moduleName) {
31514 return getMap({
31515 map: modules,
31516 keys: [type, name, moduleType, moduleName]
31517 });
31518}
31519
31520var extension = function extension() {
31521 // e.g. extension('renderer', 'svg')
31522 if (arguments.length === 2) {
31523 return getExtension.apply(null, arguments);
31524 } // e.g. extension('renderer', 'svg', { ... })
31525 else if (arguments.length === 3) {
31526 return setExtension.apply(null, arguments);
31527 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31528 else if (arguments.length === 4) {
31529 return getModule.apply(null, arguments);
31530 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31531 else if (arguments.length === 5) {
31532 return setModule.apply(null, arguments);
31533 } else {
31534 error('Invalid extension access syntax');
31535 }
31536}; // allows a core instance to access extensions internally
31537
31538
31539Core.prototype.extension = extension; // included extensions
31540
31541incExts.forEach(function (group) {
31542 group.extensions.forEach(function (ext) {
31543 setExtension(group.type, ext.name, ext.impl);
31544 });
31545});
31546
31547// (useful for init)
31548
31549var Stylesheet = function Stylesheet() {
31550 if (!(this instanceof Stylesheet)) {
31551 return new Stylesheet();
31552 }
31553
31554 this.length = 0;
31555};
31556
31557var sheetfn = Stylesheet.prototype;
31558
31559sheetfn.instanceString = function () {
31560 return 'stylesheet';
31561}; // just store the selector to be parsed later
31562
31563
31564sheetfn.selector = function (selector) {
31565 var i = this.length++;
31566 this[i] = {
31567 selector: selector,
31568 properties: []
31569 };
31570 return this; // chaining
31571}; // just store the property to be parsed later
31572
31573
31574sheetfn.css = function (name, value) {
31575 var i = this.length - 1;
31576
31577 if (string(name)) {
31578 this[i].properties.push({
31579 name: name,
31580 value: value
31581 });
31582 } else if (plainObject(name)) {
31583 var map = name;
31584 var propNames = Object.keys(map);
31585
31586 for (var j = 0; j < propNames.length; j++) {
31587 var key = propNames[j];
31588 var mapVal = map[key];
31589
31590 if (mapVal == null) {
31591 continue;
31592 }
31593
31594 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31595
31596 if (prop == null) {
31597 continue;
31598 }
31599
31600 var _name = prop.name;
31601 var _value = mapVal;
31602 this[i].properties.push({
31603 name: _name,
31604 value: _value
31605 });
31606 }
31607 }
31608
31609 return this; // chaining
31610};
31611
31612sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31613
31614sheetfn.generateStyle = function (cy) {
31615 var style = new Style(cy);
31616 return this.appendToStyle(style);
31617}; // append a dummy stylesheet object on a real style object
31618
31619
31620sheetfn.appendToStyle = function (style) {
31621 for (var i = 0; i < this.length; i++) {
31622 var context = this[i];
31623 var selector = context.selector;
31624 var props = context.properties;
31625 style.selector(selector); // apply selector
31626
31627 for (var j = 0; j < props.length; j++) {
31628 var prop = props[j];
31629 style.css(prop.name, prop.value); // apply property
31630 }
31631 }
31632
31633 return style;
31634};
31635
31636var version = "3.19.1";
31637
31638var cytoscape = function cytoscape(options) {
31639 // if no options specified, use default
31640 if (options === undefined) {
31641 options = {};
31642 } // create instance
31643
31644
31645 if (plainObject(options)) {
31646 return new Core(options);
31647 } // allow for registration of extensions
31648 else if (string(options)) {
31649 return extension.apply(extension, arguments);
31650 }
31651}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31652
31653
31654cytoscape.use = function (ext) {
31655 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31656
31657 args.unshift(cytoscape); // cytoscape is first arg to ext
31658
31659 ext.apply(null, args);
31660 return this;
31661};
31662
31663cytoscape.warnings = function (bool) {
31664 return warnings(bool);
31665}; // replaced by build system
31666
31667
31668cytoscape.version = version; // expose public apis (mostly for extensions)
31669
31670cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31671
31672export default cytoscape;