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 r.hoverData.dragged = true;
24759 } // Needs reproject due to pan changing viewport
24760
24761
24762 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24763 } else if (select[4] == 1 && (down == null || down.pannable())) {
24764 if (isOverThresholdDrag) {
24765 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24766 goIntoBoxMode();
24767 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24768 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24769
24770 if (allowPassthrough) {
24771 r.hoverData.dragging = true;
24772 r.hoverData.justStartedPan = true;
24773 select[4] = 0;
24774 r.data.bgActivePosistion = array2point(mdownPos);
24775 r.redrawHint('select', true);
24776 r.redraw();
24777 }
24778 }
24779
24780 if (down && down.pannable() && down.active()) {
24781 down.unactivate();
24782 }
24783 }
24784 } else {
24785 if (down && down.pannable() && down.active()) {
24786 down.unactivate();
24787 }
24788
24789 if ((!down || !down.grabbed()) && near != last) {
24790 if (last) {
24791 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24792 x: pos[0],
24793 y: pos[1]
24794 });
24795 }
24796
24797 if (near) {
24798 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24799 x: pos[0],
24800 y: pos[1]
24801 });
24802 }
24803
24804 r.hoverData.last = near;
24805 }
24806
24807 if (down) {
24808 if (isOverThresholdDrag) {
24809 // then we can take action
24810 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24811 // then selection overrides
24812 if (down && down.grabbed()) {
24813 freeDraggedElements(draggedElements);
24814 down.emit('freeon');
24815 draggedElements.emit('free');
24816
24817 if (r.dragData.didDrag) {
24818 down.emit('dragfreeon');
24819 draggedElements.emit('dragfree');
24820 }
24821 }
24822
24823 goIntoBoxMode();
24824 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24825 // drag node
24826 var justStartedDrag = !r.dragData.didDrag;
24827
24828 if (justStartedDrag) {
24829 r.redrawHint('eles', true);
24830 }
24831
24832 r.dragData.didDrag = true; // indicate that we actually did drag the node
24833
24834 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
24835
24836 if (!r.hoverData.draggingEles) {
24837 addNodesToDrag(draggedElements, {
24838 inDragLayer: true
24839 });
24840 }
24841
24842 var totalShift = {
24843 x: 0,
24844 y: 0
24845 };
24846
24847 if (number(disp[0]) && number(disp[1])) {
24848 totalShift.x += disp[0];
24849 totalShift.y += disp[1];
24850
24851 if (justStartedDrag) {
24852 var dragDelta = r.hoverData.dragDelta;
24853
24854 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
24855 totalShift.x += dragDelta[0];
24856 totalShift.y += dragDelta[1];
24857 }
24858 }
24859 }
24860
24861 for (var i = 0; i < draggedElements.length; i++) {
24862 var dEle = draggedElements[i];
24863
24864 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
24865 toTrigger.push(dEle);
24866 }
24867 }
24868
24869 r.hoverData.draggingEles = true;
24870 toTrigger.silentShift(totalShift).emit('position drag');
24871 r.redrawHint('drag', true);
24872 r.redraw();
24873 }
24874 } else {
24875 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
24876 updateDragDelta();
24877 }
24878 } // prevent the dragging from triggering text selection on the page
24879
24880
24881 preventDefault = true;
24882 }
24883
24884 select[2] = pos[0];
24885 select[3] = pos[1];
24886
24887 if (preventDefault) {
24888 if (e.stopPropagation) e.stopPropagation();
24889 if (e.preventDefault) e.preventDefault();
24890 return false;
24891 }
24892 }, false);
24893 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
24894 // eslint-disable-line no-undef
24895 var capture = r.hoverData.capture;
24896
24897 if (!capture) {
24898 return;
24899 }
24900
24901 r.hoverData.capture = false;
24902 var cy = r.cy;
24903 var pos = r.projectIntoViewport(e.clientX, e.clientY);
24904 var select = r.selection;
24905 var near = r.findNearestElement(pos[0], pos[1], true, false);
24906 var draggedElements = r.dragData.possibleDragElements;
24907 var down = r.hoverData.down;
24908 var multSelKeyDown = isMultSelKeyDown(e);
24909
24910 if (r.data.bgActivePosistion) {
24911 r.redrawHint('select', true);
24912 r.redraw();
24913 }
24914
24915 r.hoverData.tapholdCancelled = true;
24916 r.data.bgActivePosistion = undefined; // not active bg now
24917
24918 if (down) {
24919 down.unactivate();
24920 }
24921
24922 if (r.hoverData.which === 3) {
24923 var cxtEvt = {
24924 originalEvent: e,
24925 type: 'cxttapend',
24926 position: {
24927 x: pos[0],
24928 y: pos[1]
24929 }
24930 };
24931
24932 if (down) {
24933 down.emit(cxtEvt);
24934 } else {
24935 cy.emit(cxtEvt);
24936 }
24937
24938 if (!r.hoverData.cxtDragged) {
24939 var cxtTap = {
24940 originalEvent: e,
24941 type: 'cxttap',
24942 position: {
24943 x: pos[0],
24944 y: pos[1]
24945 }
24946 };
24947
24948 if (down) {
24949 down.emit(cxtTap);
24950 } else {
24951 cy.emit(cxtTap);
24952 }
24953 }
24954
24955 r.hoverData.cxtDragged = false;
24956 r.hoverData.which = null;
24957 } else if (r.hoverData.which === 1) {
24958 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
24959 x: pos[0],
24960 y: pos[1]
24961 });
24962
24963 if (!r.dragData.didDrag // didn't move a node around
24964 && !r.hoverData.dragged // didn't pan
24965 && !r.hoverData.selecting // not box selection
24966 && !r.hoverData.isOverThresholdDrag // didn't move too much
24967 ) {
24968 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
24969 x: pos[0],
24970 y: pos[1]
24971 });
24972 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
24973
24974
24975 if (down == null && // not mousedown on node
24976 !r.dragData.didDrag // didn't move the node around
24977 && !r.hoverData.selecting // not box selection
24978 && !r.hoverData.dragged // didn't pan
24979 && !isMultSelKeyDown(e)) {
24980 cy.$(isSelected).unselect(['tapunselect']);
24981
24982 if (draggedElements.length > 0) {
24983 r.redrawHint('eles', true);
24984 }
24985
24986 r.dragData.possibleDragElements = draggedElements = cy.collection();
24987 } // Single selection
24988
24989
24990 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
24991 if (near != null && near._private.selectable) {
24992 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
24993 if (near.selected()) {
24994 near.unselect(['tapunselect']);
24995 } else {
24996 near.select(['tapselect']);
24997 }
24998 } else {
24999 if (!multSelKeyDown) {
25000 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25001 near.select(['tapselect']);
25002 }
25003 }
25004
25005 r.redrawHint('eles', true);
25006 }
25007 }
25008
25009 if (r.hoverData.selecting) {
25010 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25011 r.redrawHint('select', true);
25012
25013 if (box.length > 0) {
25014 r.redrawHint('eles', true);
25015 }
25016
25017 cy.emit({
25018 type: 'boxend',
25019 originalEvent: e,
25020 position: {
25021 x: pos[0],
25022 y: pos[1]
25023 }
25024 });
25025
25026 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25027 return ele.selectable() && !ele.selected();
25028 };
25029
25030 if (cy.selectionType() === 'additive') {
25031 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25032 } else {
25033 if (!multSelKeyDown) {
25034 cy.$(isSelected).unmerge(box).unselect();
25035 }
25036
25037 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25038 } // always need redraw in case eles unselectable
25039
25040
25041 r.redraw();
25042 } // Cancel drag pan
25043
25044
25045 if (r.hoverData.dragging) {
25046 r.hoverData.dragging = false;
25047 r.redrawHint('select', true);
25048 r.redrawHint('eles', true);
25049 r.redraw();
25050 }
25051
25052 if (!select[4]) {
25053 r.redrawHint('drag', true);
25054 r.redrawHint('eles', true);
25055 var downWasGrabbed = down && down.grabbed();
25056 freeDraggedElements(draggedElements);
25057
25058 if (downWasGrabbed) {
25059 down.emit('freeon');
25060 draggedElements.emit('free');
25061
25062 if (r.dragData.didDrag) {
25063 down.emit('dragfreeon');
25064 draggedElements.emit('dragfree');
25065 }
25066 }
25067 }
25068 } // else not right mouse
25069
25070
25071 select[4] = 0;
25072 r.hoverData.down = null;
25073 r.hoverData.cxtStarted = false;
25074 r.hoverData.draggingEles = false;
25075 r.hoverData.selecting = false;
25076 r.hoverData.isOverThresholdDrag = false;
25077 r.dragData.didDrag = false;
25078 r.hoverData.dragged = false;
25079 r.hoverData.dragDelta = [];
25080 r.hoverData.mdownPos = null;
25081 r.hoverData.mdownGPos = null;
25082 }, false);
25083
25084 var wheelHandler = function wheelHandler(e) {
25085 if (r.scrollingPage) {
25086 return;
25087 } // while scrolling, ignore wheel-to-zoom
25088
25089
25090 var cy = r.cy;
25091 var zoom = cy.zoom();
25092 var pan = cy.pan();
25093 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25094 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25095
25096 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25097 // if pan dragging or cxt dragging, wheel movements make no zoom
25098 e.preventDefault();
25099 return;
25100 }
25101
25102 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25103 e.preventDefault();
25104 r.data.wheelZooming = true;
25105 clearTimeout(r.data.wheelTimeout);
25106 r.data.wheelTimeout = setTimeout(function () {
25107 r.data.wheelZooming = false;
25108 r.redrawHint('eles', true);
25109 r.redraw();
25110 }, 150);
25111 var diff;
25112
25113 if (e.deltaY != null) {
25114 diff = e.deltaY / -250;
25115 } else if (e.wheelDeltaY != null) {
25116 diff = e.wheelDeltaY / 1000;
25117 } else {
25118 diff = e.wheelDelta / 1000;
25119 }
25120
25121 diff = diff * r.wheelSensitivity;
25122 var needsWheelFix = e.deltaMode === 1;
25123
25124 if (needsWheelFix) {
25125 // fixes slow wheel events on ff/linux and ff/windows
25126 diff *= 33;
25127 }
25128
25129 var newZoom = cy.zoom() * Math.pow(10, diff);
25130
25131 if (e.type === 'gesturechange') {
25132 newZoom = r.gestureStartZoom * e.scale;
25133 }
25134
25135 cy.zoom({
25136 level: newZoom,
25137 renderedPosition: {
25138 x: rpos[0],
25139 y: rpos[1]
25140 }
25141 });
25142 }
25143 }; // Functions to help with whether mouse wheel should trigger zooming
25144 // --
25145
25146
25147 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25148 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25149 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25150 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25151
25152 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25153 // eslint-disable-line no-unused-vars
25154 r.scrollingPage = true;
25155 clearTimeout(r.scrollingPageTimeout);
25156 r.scrollingPageTimeout = setTimeout(function () {
25157 r.scrollingPage = false;
25158 }, 250);
25159 }, true); // desktop safari pinch to zoom start
25160
25161 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25162 r.gestureStartZoom = r.cy.zoom();
25163
25164 if (!r.hasTouchStarted) {
25165 // don't affect touch devices like iphone
25166 e.preventDefault();
25167 }
25168 }, true);
25169 r.registerBinding(r.container, 'gesturechange', function (e) {
25170 if (!r.hasTouchStarted) {
25171 // don't affect touch devices like iphone
25172 wheelHandler(e);
25173 }
25174 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25175 // Handle mouseout on Cytoscape container
25176
25177 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25178 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25179 r.cy.emit({
25180 originalEvent: e,
25181 type: 'mouseout',
25182 position: {
25183 x: pos[0],
25184 y: pos[1]
25185 }
25186 });
25187 }, false);
25188 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25189 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25190 r.cy.emit({
25191 originalEvent: e,
25192 type: 'mouseover',
25193 position: {
25194 x: pos[0],
25195 y: pos[1]
25196 }
25197 });
25198 }, false);
25199 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25200
25201 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25202
25203 var center1, modelCenter1; // center point on start pinch to zoom
25204
25205 var offsetLeft, offsetTop;
25206 var containerWidth, containerHeight;
25207 var twoFingersStartInside;
25208
25209 var distance = function distance(x1, y1, x2, y2) {
25210 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25211 };
25212
25213 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25214 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25215 };
25216
25217 var touchstartHandler;
25218 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25219 r.hasTouchStarted = true;
25220
25221 if (!eventInContainer(e)) {
25222 return;
25223 }
25224
25225 blurActiveDomElement();
25226 r.touchData.capture = true;
25227 r.data.bgActivePosistion = undefined;
25228 var cy = r.cy;
25229 var now = r.touchData.now;
25230 var earlier = r.touchData.earlier;
25231
25232 if (e.touches[0]) {
25233 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25234 now[0] = pos[0];
25235 now[1] = pos[1];
25236 }
25237
25238 if (e.touches[1]) {
25239 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25240 now[2] = pos[0];
25241 now[3] = pos[1];
25242 }
25243
25244 if (e.touches[2]) {
25245 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25246 now[4] = pos[0];
25247 now[5] = pos[1];
25248 } // record starting points for pinch-to-zoom
25249
25250
25251 if (e.touches[1]) {
25252 r.touchData.singleTouchMoved = true;
25253 freeDraggedElements(r.dragData.touchDragEles);
25254 var offsets = r.findContainerClientCoords();
25255 offsetLeft = offsets[0];
25256 offsetTop = offsets[1];
25257 containerWidth = offsets[2];
25258 containerHeight = offsets[3];
25259 f1x1 = e.touches[0].clientX - offsetLeft;
25260 f1y1 = e.touches[0].clientY - offsetTop;
25261 f2x1 = e.touches[1].clientX - offsetLeft;
25262 f2y1 = e.touches[1].clientY - offsetTop;
25263 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25264 var pan = cy.pan();
25265 var zoom = cy.zoom();
25266 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25267 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25268 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25269 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25270
25271 var cxtDistThreshold = 200;
25272 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25273
25274 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25275 var near1 = r.findNearestElement(now[0], now[1], true, true);
25276 var near2 = r.findNearestElement(now[2], now[3], true, true);
25277
25278 if (near1 && near1.isNode()) {
25279 near1.activate().emit({
25280 originalEvent: e,
25281 type: 'cxttapstart',
25282 position: {
25283 x: now[0],
25284 y: now[1]
25285 }
25286 });
25287 r.touchData.start = near1;
25288 } else if (near2 && near2.isNode()) {
25289 near2.activate().emit({
25290 originalEvent: e,
25291 type: 'cxttapstart',
25292 position: {
25293 x: now[0],
25294 y: now[1]
25295 }
25296 });
25297 r.touchData.start = near2;
25298 } else {
25299 cy.emit({
25300 originalEvent: e,
25301 type: 'cxttapstart',
25302 position: {
25303 x: now[0],
25304 y: now[1]
25305 }
25306 });
25307 }
25308
25309 if (r.touchData.start) {
25310 r.touchData.start._private.grabbed = false;
25311 }
25312
25313 r.touchData.cxt = true;
25314 r.touchData.cxtDragged = false;
25315 r.data.bgActivePosistion = undefined;
25316 r.redraw();
25317 return;
25318 }
25319 }
25320
25321 if (e.touches[2]) {
25322 // ignore
25323 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25324 if (cy.boxSelectionEnabled()) {
25325 e.preventDefault();
25326 }
25327 } else if (e.touches[1]) ; else if (e.touches[0]) {
25328 var nears = r.findNearestElements(now[0], now[1], true, true);
25329 var near = nears[0];
25330
25331 if (near != null) {
25332 near.activate();
25333 r.touchData.start = near;
25334 r.touchData.starts = nears;
25335
25336 if (r.nodeIsGrabbable(near)) {
25337 var draggedEles = r.dragData.touchDragEles = cy.collection();
25338 var selectedNodes = null;
25339 r.redrawHint('eles', true);
25340 r.redrawHint('drag', true);
25341
25342 if (near.selected()) {
25343 // reset drag elements, since near will be added again
25344 selectedNodes = cy.$(function (ele) {
25345 return ele.selected() && r.nodeIsGrabbable(ele);
25346 });
25347 addNodesToDrag(selectedNodes, {
25348 addToList: draggedEles
25349 });
25350 } else {
25351 addNodeToDrag(near, {
25352 addToList: draggedEles
25353 });
25354 }
25355
25356 setGrabTarget(near);
25357
25358 var makeEvent = function makeEvent(type) {
25359 return {
25360 originalEvent: e,
25361 type: type,
25362 position: {
25363 x: now[0],
25364 y: now[1]
25365 }
25366 };
25367 };
25368
25369 near.emit(makeEvent('grabon'));
25370
25371 if (selectedNodes) {
25372 selectedNodes.forEach(function (n) {
25373 n.emit(makeEvent('grab'));
25374 });
25375 } else {
25376 near.emit(makeEvent('grab'));
25377 }
25378 }
25379 }
25380
25381 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25382 x: now[0],
25383 y: now[1]
25384 });
25385
25386 if (near == null) {
25387 r.data.bgActivePosistion = {
25388 x: pos[0],
25389 y: pos[1]
25390 };
25391 r.redrawHint('select', true);
25392 r.redraw();
25393 } // Tap, taphold
25394 // -----
25395
25396
25397 r.touchData.singleTouchMoved = false;
25398 r.touchData.singleTouchStartTime = +new Date();
25399 clearTimeout(r.touchData.tapholdTimeout);
25400 r.touchData.tapholdTimeout = setTimeout(function () {
25401 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25402 && !r.touchData.selecting // box selection shouldn't allow taphold through
25403 ) {
25404 triggerEvents(r.touchData.start, ['taphold'], e, {
25405 x: now[0],
25406 y: now[1]
25407 });
25408 }
25409 }, r.tapholdDuration);
25410 }
25411
25412 if (e.touches.length >= 1) {
25413 var sPos = r.touchData.startPosition = [];
25414
25415 for (var i = 0; i < now.length; i++) {
25416 sPos[i] = earlier[i] = now[i];
25417 }
25418
25419 var touch0 = e.touches[0];
25420 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25421 }
25422 }, false);
25423 var touchmoveHandler;
25424 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25425 // eslint-disable-line no-undef
25426 var capture = r.touchData.capture;
25427
25428 if (!capture && !eventInContainer(e)) {
25429 return;
25430 }
25431
25432 var select = r.selection;
25433 var cy = r.cy;
25434 var now = r.touchData.now;
25435 var earlier = r.touchData.earlier;
25436 var zoom = cy.zoom();
25437
25438 if (e.touches[0]) {
25439 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25440 now[0] = pos[0];
25441 now[1] = pos[1];
25442 }
25443
25444 if (e.touches[1]) {
25445 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25446 now[2] = pos[0];
25447 now[3] = pos[1];
25448 }
25449
25450 if (e.touches[2]) {
25451 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25452 now[4] = pos[0];
25453 now[5] = pos[1];
25454 }
25455
25456 var startGPos = r.touchData.startGPosition;
25457 var isOverThresholdDrag;
25458
25459 if (capture && e.touches[0] && startGPos) {
25460 var disp = [];
25461
25462 for (var j = 0; j < now.length; j++) {
25463 disp[j] = now[j] - earlier[j];
25464 }
25465
25466 var dx = e.touches[0].clientX - startGPos[0];
25467 var dx2 = dx * dx;
25468 var dy = e.touches[0].clientY - startGPos[1];
25469 var dy2 = dy * dy;
25470 var dist2 = dx2 + dy2;
25471 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25472 } // context swipe cancelling
25473
25474
25475 if (capture && r.touchData.cxt) {
25476 e.preventDefault();
25477 var f1x2 = e.touches[0].clientX - offsetLeft,
25478 f1y2 = e.touches[0].clientY - offsetTop;
25479 var f2x2 = e.touches[1].clientX - offsetLeft,
25480 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25481
25482 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25483 var factorSq = distance2Sq / distance1Sq;
25484 var distThreshold = 150;
25485 var distThresholdSq = distThreshold * distThreshold;
25486 var factorThreshold = 1.5;
25487 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25488
25489 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25490 r.touchData.cxt = false;
25491 r.data.bgActivePosistion = undefined;
25492 r.redrawHint('select', true);
25493 var cxtEvt = {
25494 originalEvent: e,
25495 type: 'cxttapend',
25496 position: {
25497 x: now[0],
25498 y: now[1]
25499 }
25500 };
25501
25502 if (r.touchData.start) {
25503 r.touchData.start.unactivate().emit(cxtEvt);
25504 r.touchData.start = null;
25505 } else {
25506 cy.emit(cxtEvt);
25507 }
25508 }
25509 } // context swipe
25510
25511
25512 if (capture && r.touchData.cxt) {
25513 var cxtEvt = {
25514 originalEvent: e,
25515 type: 'cxtdrag',
25516 position: {
25517 x: now[0],
25518 y: now[1]
25519 }
25520 };
25521 r.data.bgActivePosistion = undefined;
25522 r.redrawHint('select', true);
25523
25524 if (r.touchData.start) {
25525 r.touchData.start.emit(cxtEvt);
25526 } else {
25527 cy.emit(cxtEvt);
25528 }
25529
25530 if (r.touchData.start) {
25531 r.touchData.start._private.grabbed = false;
25532 }
25533
25534 r.touchData.cxtDragged = true;
25535 var near = r.findNearestElement(now[0], now[1], true, true);
25536
25537 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25538 if (r.touchData.cxtOver) {
25539 r.touchData.cxtOver.emit({
25540 originalEvent: e,
25541 type: 'cxtdragout',
25542 position: {
25543 x: now[0],
25544 y: now[1]
25545 }
25546 });
25547 }
25548
25549 r.touchData.cxtOver = near;
25550
25551 if (near) {
25552 near.emit({
25553 originalEvent: e,
25554 type: 'cxtdragover',
25555 position: {
25556 x: now[0],
25557 y: now[1]
25558 }
25559 });
25560 }
25561 } // box selection
25562
25563 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25564 e.preventDefault();
25565 r.data.bgActivePosistion = undefined;
25566 this.lastThreeTouch = +new Date();
25567
25568 if (!r.touchData.selecting) {
25569 cy.emit({
25570 originalEvent: e,
25571 type: 'boxstart',
25572 position: {
25573 x: now[0],
25574 y: now[1]
25575 }
25576 });
25577 }
25578
25579 r.touchData.selecting = true;
25580 r.touchData.didSelect = true;
25581 select[4] = 1;
25582
25583 if (!select || select.length === 0 || select[0] === undefined) {
25584 select[0] = (now[0] + now[2] + now[4]) / 3;
25585 select[1] = (now[1] + now[3] + now[5]) / 3;
25586 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25587 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25588 } else {
25589 select[2] = (now[0] + now[2] + now[4]) / 3;
25590 select[3] = (now[1] + now[3] + now[5]) / 3;
25591 }
25592
25593 r.redrawHint('select', true);
25594 r.redraw(); // pinch to zoom
25595 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25596 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25597 // two fingers => pinch to zoom
25598 e.preventDefault();
25599 r.data.bgActivePosistion = undefined;
25600 r.redrawHint('select', true);
25601 var draggedEles = r.dragData.touchDragEles;
25602
25603 if (draggedEles) {
25604 r.redrawHint('drag', true);
25605
25606 for (var i = 0; i < draggedEles.length; i++) {
25607 var de_p = draggedEles[i]._private;
25608 de_p.grabbed = false;
25609 de_p.rscratch.inDragLayer = false;
25610 }
25611 }
25612
25613 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25614
25615 var f1x2 = e.touches[0].clientX - offsetLeft,
25616 f1y2 = e.touches[0].clientY - offsetTop;
25617 var f2x2 = e.touches[1].clientX - offsetLeft,
25618 f2y2 = e.touches[1].clientY - offsetTop;
25619 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25620 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25621
25622 var factor = distance2 / distance1;
25623
25624 if (twoFingersStartInside) {
25625 // delta finger1
25626 var df1x = f1x2 - f1x1;
25627 var df1y = f1y2 - f1y1; // delta finger 2
25628
25629 var df2x = f2x2 - f2x1;
25630 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25631 // i.e. so pinching cancels out and moving together pans
25632
25633 var tx = (df1x + df2x) / 2;
25634 var ty = (df1y + df2y) / 2; // now calculate the zoom
25635
25636 var zoom1 = cy.zoom();
25637 var zoom2 = zoom1 * factor;
25638 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25639
25640 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25641 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25642 var pan2 = {
25643 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25644 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25645 }; // remove dragged eles
25646
25647 if (_start && _start.active()) {
25648 var draggedEles = r.dragData.touchDragEles;
25649 freeDraggedElements(draggedEles);
25650 r.redrawHint('drag', true);
25651 r.redrawHint('eles', true);
25652
25653 _start.unactivate().emit('freeon');
25654
25655 draggedEles.emit('free');
25656
25657 if (r.dragData.didDrag) {
25658 _start.emit('dragfreeon');
25659
25660 draggedEles.emit('dragfree');
25661 }
25662 }
25663
25664 cy.viewport({
25665 zoom: zoom2,
25666 pan: pan2,
25667 cancelOnFailedZoom: true
25668 });
25669 distance1 = distance2;
25670 f1x1 = f1x2;
25671 f1y1 = f1y2;
25672 f2x1 = f2x2;
25673 f2y1 = f2y2;
25674 r.pinching = true;
25675 } // Re-project
25676
25677
25678 if (e.touches[0]) {
25679 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25680 now[0] = pos[0];
25681 now[1] = pos[1];
25682 }
25683
25684 if (e.touches[1]) {
25685 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25686 now[2] = pos[0];
25687 now[3] = pos[1];
25688 }
25689
25690 if (e.touches[2]) {
25691 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25692 now[4] = pos[0];
25693 now[5] = pos[1];
25694 }
25695 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25696 ) {
25697 var start = r.touchData.start;
25698 var last = r.touchData.last;
25699 var near;
25700
25701 if (!r.hoverData.draggingEles && !r.swipePanning) {
25702 near = r.findNearestElement(now[0], now[1], true, true);
25703 }
25704
25705 if (capture && start != null) {
25706 e.preventDefault();
25707 } // dragging nodes
25708
25709
25710 if (capture && start != null && r.nodeIsDraggable(start)) {
25711 if (isOverThresholdDrag) {
25712 // then dragging can happen
25713 var draggedEles = r.dragData.touchDragEles;
25714 var justStartedDrag = !r.dragData.didDrag;
25715
25716 if (justStartedDrag) {
25717 addNodesToDrag(draggedEles, {
25718 inDragLayer: true
25719 });
25720 }
25721
25722 r.dragData.didDrag = true;
25723 var totalShift = {
25724 x: 0,
25725 y: 0
25726 };
25727
25728 if (number(disp[0]) && number(disp[1])) {
25729 totalShift.x += disp[0];
25730 totalShift.y += disp[1];
25731
25732 if (justStartedDrag) {
25733 r.redrawHint('eles', true);
25734 var dragDelta = r.touchData.dragDelta;
25735
25736 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25737 totalShift.x += dragDelta[0];
25738 totalShift.y += dragDelta[1];
25739 }
25740 }
25741 }
25742
25743 r.hoverData.draggingEles = true;
25744 draggedEles.silentShift(totalShift).emit('position drag');
25745 r.redrawHint('drag', true);
25746
25747 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25748 r.redrawHint('eles', true);
25749 }
25750
25751 r.redraw();
25752 } else {
25753 // otherise keep track of drag delta for later
25754 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25755
25756 if (dragDelta.length === 0) {
25757 dragDelta.push(disp[0]);
25758 dragDelta.push(disp[1]);
25759 } else {
25760 dragDelta[0] += disp[0];
25761 dragDelta[1] += disp[1];
25762 }
25763 }
25764 } // touchmove
25765
25766
25767 {
25768 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25769 x: now[0],
25770 y: now[1]
25771 });
25772
25773 if ((!start || !start.grabbed()) && near != last) {
25774 if (last) {
25775 last.emit({
25776 originalEvent: e,
25777 type: 'tapdragout',
25778 position: {
25779 x: now[0],
25780 y: now[1]
25781 }
25782 });
25783 }
25784
25785 if (near) {
25786 near.emit({
25787 originalEvent: e,
25788 type: 'tapdragover',
25789 position: {
25790 x: now[0],
25791 y: now[1]
25792 }
25793 });
25794 }
25795 }
25796
25797 r.touchData.last = near;
25798 } // check to cancel taphold
25799
25800 if (capture) {
25801 for (var i = 0; i < now.length; i++) {
25802 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25803 r.touchData.singleTouchMoved = true;
25804 }
25805 }
25806 } // panning
25807
25808
25809 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25810 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25811
25812 if (allowPassthrough) {
25813 e.preventDefault();
25814
25815 if (!r.data.bgActivePosistion) {
25816 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25817 }
25818
25819 if (r.swipePanning) {
25820 cy.panBy({
25821 x: disp[0] * zoom,
25822 y: disp[1] * zoom
25823 });
25824 } else if (isOverThresholdDrag) {
25825 r.swipePanning = true;
25826 cy.panBy({
25827 x: dx * zoom,
25828 y: dy * zoom
25829 });
25830
25831 if (start) {
25832 start.unactivate();
25833 r.redrawHint('select', true);
25834 r.touchData.start = null;
25835 }
25836 }
25837 } // Re-project
25838
25839
25840 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25841 now[0] = pos[0];
25842 now[1] = pos[1];
25843 }
25844 }
25845
25846 for (var j = 0; j < now.length; j++) {
25847 earlier[j] = now[j];
25848 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25849
25850
25851 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
25852 r.data.bgActivePosistion = undefined;
25853 r.redrawHint('select', true);
25854 r.redraw();
25855 }
25856 }, false);
25857 var touchcancelHandler;
25858 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
25859 // eslint-disable-line no-unused-vars
25860 var start = r.touchData.start;
25861 r.touchData.capture = false;
25862
25863 if (start) {
25864 start.unactivate();
25865 }
25866 });
25867 var touchendHandler;
25868 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
25869 // eslint-disable-line no-unused-vars
25870 var start = r.touchData.start;
25871 var capture = r.touchData.capture;
25872
25873 if (capture) {
25874 if (e.touches.length === 0) {
25875 r.touchData.capture = false;
25876 }
25877
25878 e.preventDefault();
25879 } else {
25880 return;
25881 }
25882
25883 var select = r.selection;
25884 r.swipePanning = false;
25885 r.hoverData.draggingEles = false;
25886 var cy = r.cy;
25887 var zoom = cy.zoom();
25888 var now = r.touchData.now;
25889 var earlier = r.touchData.earlier;
25890
25891 if (e.touches[0]) {
25892 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25893 now[0] = pos[0];
25894 now[1] = pos[1];
25895 }
25896
25897 if (e.touches[1]) {
25898 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25899 now[2] = pos[0];
25900 now[3] = pos[1];
25901 }
25902
25903 if (e.touches[2]) {
25904 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25905 now[4] = pos[0];
25906 now[5] = pos[1];
25907 }
25908
25909 if (start) {
25910 start.unactivate();
25911 }
25912
25913 var ctxTapend;
25914
25915 if (r.touchData.cxt) {
25916 ctxTapend = {
25917 originalEvent: e,
25918 type: 'cxttapend',
25919 position: {
25920 x: now[0],
25921 y: now[1]
25922 }
25923 };
25924
25925 if (start) {
25926 start.emit(ctxTapend);
25927 } else {
25928 cy.emit(ctxTapend);
25929 }
25930
25931 if (!r.touchData.cxtDragged) {
25932 var ctxTap = {
25933 originalEvent: e,
25934 type: 'cxttap',
25935 position: {
25936 x: now[0],
25937 y: now[1]
25938 }
25939 };
25940
25941 if (start) {
25942 start.emit(ctxTap);
25943 } else {
25944 cy.emit(ctxTap);
25945 }
25946 }
25947
25948 if (r.touchData.start) {
25949 r.touchData.start._private.grabbed = false;
25950 }
25951
25952 r.touchData.cxt = false;
25953 r.touchData.start = null;
25954 r.redraw();
25955 return;
25956 } // no more box selection if we don't have three fingers
25957
25958
25959 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
25960 r.touchData.selecting = false;
25961 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25962 select[0] = undefined;
25963 select[1] = undefined;
25964 select[2] = undefined;
25965 select[3] = undefined;
25966 select[4] = 0;
25967 r.redrawHint('select', true);
25968 cy.emit({
25969 type: 'boxend',
25970 originalEvent: e,
25971 position: {
25972 x: now[0],
25973 y: now[1]
25974 }
25975 });
25976
25977 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25978 return ele.selectable() && !ele.selected();
25979 };
25980
25981 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25982
25983 if (box.nonempty()) {
25984 r.redrawHint('eles', true);
25985 }
25986
25987 r.redraw();
25988 }
25989
25990 if (start != null) {
25991 start.unactivate();
25992 }
25993
25994 if (e.touches[2]) {
25995 r.data.bgActivePosistion = undefined;
25996 r.redrawHint('select', true);
25997 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
25998 r.data.bgActivePosistion = undefined;
25999 r.redrawHint('select', true);
26000 var draggedEles = r.dragData.touchDragEles;
26001
26002 if (start != null) {
26003 var startWasGrabbed = start._private.grabbed;
26004 freeDraggedElements(draggedEles);
26005 r.redrawHint('drag', true);
26006 r.redrawHint('eles', true);
26007
26008 if (startWasGrabbed) {
26009 start.emit('freeon');
26010 draggedEles.emit('free');
26011
26012 if (r.dragData.didDrag) {
26013 start.emit('dragfreeon');
26014 draggedEles.emit('dragfree');
26015 }
26016 }
26017
26018 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26019 x: now[0],
26020 y: now[1]
26021 });
26022 start.unactivate();
26023 r.touchData.start = null;
26024 } else {
26025 var near = r.findNearestElement(now[0], now[1], true, true);
26026 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26027 x: now[0],
26028 y: now[1]
26029 });
26030 }
26031
26032 var dx = r.touchData.startPosition[0] - now[0];
26033 var dx2 = dx * dx;
26034 var dy = r.touchData.startPosition[1] - now[1];
26035 var dy2 = dy * dy;
26036 var dist2 = dx2 + dy2;
26037 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26038
26039 if (!r.touchData.singleTouchMoved) {
26040 if (!start) {
26041 cy.$(':selected').unselect(['tapunselect']);
26042 }
26043
26044 triggerEvents(start, ['tap', 'vclick'], e, {
26045 x: now[0],
26046 y: now[1]
26047 });
26048 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26049
26050
26051 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26052 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26053 ) {
26054 if (cy.selectionType() === 'single') {
26055 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26056 start.select(['tapselect']);
26057 } else {
26058 if (start.selected()) {
26059 start.unselect(['tapunselect']);
26060 } else {
26061 start.select(['tapselect']);
26062 }
26063 }
26064
26065 r.redrawHint('eles', true);
26066 }
26067
26068 r.touchData.singleTouchMoved = true;
26069 }
26070
26071 for (var j = 0; j < now.length; j++) {
26072 earlier[j] = now[j];
26073 }
26074
26075 r.dragData.didDrag = false; // reset for next touchstart
26076
26077 if (e.touches.length === 0) {
26078 r.touchData.dragDelta = [];
26079 r.touchData.startPosition = null;
26080 r.touchData.startGPosition = null;
26081 r.touchData.didSelect = false;
26082 }
26083
26084 if (e.touches.length < 2) {
26085 if (e.touches.length === 1) {
26086 // the old start global pos'n may not be the same finger that remains
26087 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26088 }
26089
26090 r.pinching = false;
26091 r.redrawHint('eles', true);
26092 r.redraw();
26093 } //r.redraw();
26094
26095 }, false); // fallback compatibility layer for ms pointer events
26096
26097 if (typeof TouchEvent === 'undefined') {
26098 var pointers = [];
26099
26100 var makeTouch = function makeTouch(e) {
26101 return {
26102 clientX: e.clientX,
26103 clientY: e.clientY,
26104 force: 1,
26105 identifier: e.pointerId,
26106 pageX: e.pageX,
26107 pageY: e.pageY,
26108 radiusX: e.width / 2,
26109 radiusY: e.height / 2,
26110 screenX: e.screenX,
26111 screenY: e.screenY,
26112 target: e.target
26113 };
26114 };
26115
26116 var makePointer = function makePointer(e) {
26117 return {
26118 event: e,
26119 touch: makeTouch(e)
26120 };
26121 };
26122
26123 var addPointer = function addPointer(e) {
26124 pointers.push(makePointer(e));
26125 };
26126
26127 var removePointer = function removePointer(e) {
26128 for (var i = 0; i < pointers.length; i++) {
26129 var p = pointers[i];
26130
26131 if (p.event.pointerId === e.pointerId) {
26132 pointers.splice(i, 1);
26133 return;
26134 }
26135 }
26136 };
26137
26138 var updatePointer = function updatePointer(e) {
26139 var p = pointers.filter(function (p) {
26140 return p.event.pointerId === e.pointerId;
26141 })[0];
26142 p.event = e;
26143 p.touch = makeTouch(e);
26144 };
26145
26146 var addTouchesToEvent = function addTouchesToEvent(e) {
26147 e.touches = pointers.map(function (p) {
26148 return p.touch;
26149 });
26150 };
26151
26152 var pointerIsMouse = function pointerIsMouse(e) {
26153 return e.pointerType === 'mouse' || e.pointerType === 4;
26154 };
26155
26156 r.registerBinding(r.container, 'pointerdown', function (e) {
26157 if (pointerIsMouse(e)) {
26158 return;
26159 } // mouse already handled
26160
26161
26162 e.preventDefault();
26163 addPointer(e);
26164 addTouchesToEvent(e);
26165 touchstartHandler(e);
26166 });
26167 r.registerBinding(r.container, 'pointerup', function (e) {
26168 if (pointerIsMouse(e)) {
26169 return;
26170 } // mouse already handled
26171
26172
26173 removePointer(e);
26174 addTouchesToEvent(e);
26175 touchendHandler(e);
26176 });
26177 r.registerBinding(r.container, 'pointercancel', function (e) {
26178 if (pointerIsMouse(e)) {
26179 return;
26180 } // mouse already handled
26181
26182
26183 removePointer(e);
26184 addTouchesToEvent(e);
26185 touchcancelHandler(e);
26186 });
26187 r.registerBinding(r.container, 'pointermove', function (e) {
26188 if (pointerIsMouse(e)) {
26189 return;
26190 } // mouse already handled
26191
26192
26193 e.preventDefault();
26194 updatePointer(e);
26195 addTouchesToEvent(e);
26196 touchmoveHandler(e);
26197 });
26198 }
26199};
26200
26201var BRp$d = {};
26202
26203BRp$d.generatePolygon = function (name, points) {
26204 return this.nodeShapes[name] = {
26205 renderer: this,
26206 name: name,
26207 points: points,
26208 draw: function draw(context, centerX, centerY, width, height) {
26209 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26210 },
26211 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26212 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26213 },
26214 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26215 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26216 }
26217 };
26218};
26219
26220BRp$d.generateEllipse = function () {
26221 return this.nodeShapes['ellipse'] = {
26222 renderer: this,
26223 name: 'ellipse',
26224 draw: function draw(context, centerX, centerY, width, height) {
26225 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26226 },
26227 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26228 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26229 },
26230 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26231 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26232 }
26233 };
26234};
26235
26236BRp$d.generateRoundPolygon = function (name, points) {
26237 // Pre-compute control points
26238 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26239 // the unit vectors.
26240 // For simplicity the layout will be:
26241 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26242 var allPoints = new Array(points.length * 2);
26243
26244 for (var i = 0; i < points.length / 2; i++) {
26245 var sourceIndex = i * 2;
26246 var destIndex = void 0;
26247
26248 if (i < points.length / 2 - 1) {
26249 destIndex = (i + 1) * 2;
26250 } else {
26251 destIndex = 0;
26252 }
26253
26254 allPoints[i * 4] = points[sourceIndex];
26255 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26256 var xDest = points[destIndex] - points[sourceIndex];
26257 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26258 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26259 allPoints[i * 4 + 2] = xDest / norm;
26260 allPoints[i * 4 + 3] = yDest / norm;
26261 }
26262
26263 return this.nodeShapes[name] = {
26264 renderer: this,
26265 name: name,
26266 points: allPoints,
26267 draw: function draw(context, centerX, centerY, width, height) {
26268 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26269 },
26270 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26271 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26272 },
26273 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26274 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26275 }
26276 };
26277};
26278
26279BRp$d.generateRoundRectangle = function () {
26280 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26281 renderer: this,
26282 name: 'round-rectangle',
26283 points: generateUnitNgonPointsFitToSquare(4, 0),
26284 draw: function draw(context, centerX, centerY, width, height) {
26285 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26286 },
26287 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26288 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26289 },
26290 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26291 var cornerRadius = getRoundRectangleRadius(width, height);
26292 var diam = cornerRadius * 2; // Check hBox
26293
26294 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26295 return true;
26296 } // Check vBox
26297
26298
26299 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26300 return true;
26301 } // Check top left quarter circle
26302
26303
26304 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26305 return true;
26306 } // Check top right 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 bottom 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 left quarter circle
26317
26318
26319 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26320 return true;
26321 }
26322
26323 return false;
26324 }
26325 };
26326};
26327
26328BRp$d.generateCutRectangle = function () {
26329 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26330 renderer: this,
26331 name: 'cut-rectangle',
26332 cornerLength: getCutRectangleCornerLength(),
26333 points: generateUnitNgonPointsFitToSquare(4, 0),
26334 draw: function draw(context, centerX, centerY, width, height) {
26335 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26336 },
26337 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
26338 var cl = this.cornerLength;
26339 var hh = height / 2;
26340 var hw = width / 2;
26341 var xBegin = centerX - hw;
26342 var xEnd = centerX + hw;
26343 var yBegin = centerY - hh;
26344 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26345
26346 return {
26347 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26348 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26349 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26350 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26351 };
26352 },
26353 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26354 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26355 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26356 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26357 },
26358 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26359 // Check hBox
26360 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
26361 return true;
26362 } // Check vBox
26363
26364
26365 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
26366 return true;
26367 }
26368
26369 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26370 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26371 }
26372 };
26373};
26374
26375BRp$d.generateBarrel = function () {
26376 return this.nodeShapes['barrel'] = {
26377 renderer: this,
26378 name: 'barrel',
26379 points: generateUnitNgonPointsFitToSquare(4, 0),
26380 draw: function draw(context, centerX, centerY, width, height) {
26381 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26382 },
26383 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26384 // use two fixed t values for the bezier curve approximation
26385 var t0 = 0.15;
26386 var t1 = 0.5;
26387 var t2 = 0.85;
26388 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26389
26390 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26391 // approximate curve pts based on the two t values
26392 var m0 = qbezierPtAt({
26393 x: pts[0],
26394 y: pts[1]
26395 }, {
26396 x: pts[2],
26397 y: pts[3]
26398 }, {
26399 x: pts[4],
26400 y: pts[5]
26401 }, t0);
26402 var m1 = qbezierPtAt({
26403 x: pts[0],
26404 y: pts[1]
26405 }, {
26406 x: pts[2],
26407 y: pts[3]
26408 }, {
26409 x: pts[4],
26410 y: pts[5]
26411 }, t1);
26412 var m2 = qbezierPtAt({
26413 x: pts[0],
26414 y: pts[1]
26415 }, {
26416 x: pts[2],
26417 y: pts[3]
26418 }, {
26419 x: pts[4],
26420 y: pts[5]
26421 }, t2);
26422 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26423 };
26424
26425 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26426 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26427 },
26428 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26429 var hh = height / 2;
26430 var hw = width / 2;
26431 var xBegin = centerX - hw;
26432 var xEnd = centerX + hw;
26433 var yBegin = centerY - hh;
26434 var yEnd = centerY + hh;
26435 var curveConstants = getBarrelCurveConstants(width, height);
26436 var hOffset = curveConstants.heightOffset;
26437 var wOffset = curveConstants.widthOffset;
26438 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26439
26440 var pts = {
26441 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26442 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26443 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26444 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26445 };
26446 pts.topLeft.isTop = true;
26447 pts.topRight.isTop = true;
26448 pts.bottomLeft.isBottom = true;
26449 pts.bottomRight.isBottom = true;
26450 return pts;
26451 },
26452 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26453 var curveConstants = getBarrelCurveConstants(width, height);
26454 var hOffset = curveConstants.heightOffset;
26455 var wOffset = curveConstants.widthOffset; // Check hBox
26456
26457 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26458 return true;
26459 } // Check vBox
26460
26461
26462 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26463 return true;
26464 }
26465
26466 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26467
26468 var getCurveT = function getCurveT(x, y, curvePts) {
26469 var x0 = curvePts[4];
26470 var x1 = curvePts[2];
26471 var x2 = curvePts[0];
26472 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26473
26474 var y2 = curvePts[1];
26475 var xMin = Math.min(x0, x2);
26476 var xMax = Math.max(x0, x2);
26477 var yMin = Math.min(y0, y2);
26478 var yMax = Math.max(y0, y2);
26479
26480 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26481 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26482 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26483 var validRoots = roots.filter(function (r) {
26484 return 0 <= r && r <= 1;
26485 });
26486
26487 if (validRoots.length > 0) {
26488 return validRoots[0];
26489 }
26490 }
26491
26492 return null;
26493 };
26494
26495 var curveRegions = Object.keys(barrelCurvePts);
26496
26497 for (var i = 0; i < curveRegions.length; i++) {
26498 var corner = curveRegions[i];
26499 var cornerPts = barrelCurvePts[corner];
26500 var t = getCurveT(x, y, cornerPts);
26501
26502 if (t == null) {
26503 continue;
26504 }
26505
26506 var y0 = cornerPts[5];
26507 var y1 = cornerPts[3];
26508 var y2 = cornerPts[1];
26509 var bezY = qbezierAt(y0, y1, y2, t);
26510
26511 if (cornerPts.isTop && bezY <= y) {
26512 return true;
26513 }
26514
26515 if (cornerPts.isBottom && y <= bezY) {
26516 return true;
26517 }
26518 }
26519
26520 return false;
26521 }
26522 };
26523};
26524
26525BRp$d.generateBottomRoundrectangle = function () {
26526 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26527 renderer: this,
26528 name: 'bottom-round-rectangle',
26529 points: generateUnitNgonPointsFitToSquare(4, 0),
26530 draw: function draw(context, centerX, centerY, width, height) {
26531 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26532 },
26533 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26534 var topStartX = nodeX - (width / 2 + padding);
26535 var topStartY = nodeY - (height / 2 + padding);
26536 var topEndY = topStartY;
26537 var topEndX = nodeX + (width / 2 + padding);
26538 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26539
26540 if (topIntersections.length > 0) {
26541 return topIntersections;
26542 }
26543
26544 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26545 },
26546 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26547 var cornerRadius = getRoundRectangleRadius(width, height);
26548 var diam = 2 * cornerRadius; // Check hBox
26549
26550 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26551 return true;
26552 } // Check vBox
26553
26554
26555 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26556 return true;
26557 } // check non-rounded top side
26558
26559
26560 var outerWidth = width / 2 + 2 * padding;
26561 var outerHeight = height / 2 + 2 * padding;
26562 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26563
26564 if (pointInsidePolygonPoints(x, y, points)) {
26565 return true;
26566 } // Check bottom right quarter circle
26567
26568
26569 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26570 return true;
26571 } // Check bottom left quarter circle
26572
26573
26574 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26575 return true;
26576 }
26577
26578 return false;
26579 }
26580 };
26581};
26582
26583BRp$d.registerNodeShapes = function () {
26584 var nodeShapes = this.nodeShapes = {};
26585 var renderer = this;
26586 this.generateEllipse();
26587 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26588 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26589 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26590 nodeShapes['square'] = nodeShapes['rectangle'];
26591 this.generateRoundRectangle();
26592 this.generateCutRectangle();
26593 this.generateBarrel();
26594 this.generateBottomRoundrectangle();
26595 {
26596 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26597 this.generatePolygon('diamond', diamondPoints);
26598 this.generateRoundPolygon('round-diamond', diamondPoints);
26599 }
26600 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26601 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26602 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26603 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26604 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26605 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26606 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26607 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26608 var star5Points = new Array(20);
26609 {
26610 var outerPoints = generateUnitNgonPoints(5, 0);
26611 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26612
26613 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26614 innerRadius *= 1.57;
26615
26616 for (var i = 0; i < innerPoints.length / 2; i++) {
26617 innerPoints[i * 2] *= innerRadius;
26618 innerPoints[i * 2 + 1] *= innerRadius;
26619 }
26620
26621 for (var i = 0; i < 20 / 4; i++) {
26622 star5Points[i * 4] = outerPoints[i * 2];
26623 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26624 star5Points[i * 4 + 2] = innerPoints[i * 2];
26625 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26626 }
26627 }
26628 star5Points = fitPolygonToSquare(star5Points);
26629 this.generatePolygon('star', star5Points);
26630 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26631 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26632 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]);
26633 {
26634 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26635 this.generatePolygon('tag', tagPoints);
26636 this.generateRoundPolygon('round-tag', tagPoints);
26637 }
26638
26639 nodeShapes.makePolygon = function (points) {
26640 // use caching on user-specified polygons so they are as fast as native shapes
26641 var key = points.join('$');
26642 var name = 'polygon-' + key;
26643 var shape;
26644
26645 if (shape = this[name]) {
26646 // got cached shape
26647 return shape;
26648 } // create and cache new shape
26649
26650
26651 return renderer.generatePolygon(name, points);
26652 };
26653};
26654
26655var BRp$e = {};
26656
26657BRp$e.timeToRender = function () {
26658 return this.redrawTotalTime / this.redrawCount;
26659};
26660
26661BRp$e.redraw = function (options) {
26662 options = options || staticEmptyObject();
26663 var r = this;
26664
26665 if (r.averageRedrawTime === undefined) {
26666 r.averageRedrawTime = 0;
26667 }
26668
26669 if (r.lastRedrawTime === undefined) {
26670 r.lastRedrawTime = 0;
26671 }
26672
26673 if (r.lastDrawTime === undefined) {
26674 r.lastDrawTime = 0;
26675 }
26676
26677 r.requestedFrame = true;
26678 r.renderOptions = options;
26679};
26680
26681BRp$e.beforeRender = function (fn, priority) {
26682 // the renderer can't add tick callbacks when destroyed
26683 if (this.destroyed) {
26684 return;
26685 }
26686
26687 if (priority == null) {
26688 error('Priority is not optional for beforeRender');
26689 }
26690
26691 var cbs = this.beforeRenderCallbacks;
26692 cbs.push({
26693 fn: fn,
26694 priority: priority
26695 }); // higher priority callbacks executed first
26696
26697 cbs.sort(function (a, b) {
26698 return b.priority - a.priority;
26699 });
26700};
26701
26702var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26703 var cbs = r.beforeRenderCallbacks;
26704
26705 for (var i = 0; i < cbs.length; i++) {
26706 cbs[i].fn(willDraw, startTime);
26707 }
26708};
26709
26710BRp$e.startRenderLoop = function () {
26711 var r = this;
26712 var cy = r.cy;
26713
26714 if (r.renderLoopStarted) {
26715 return;
26716 } else {
26717 r.renderLoopStarted = true;
26718 }
26719
26720 var renderFn = function renderFn(requestTime) {
26721 if (r.destroyed) {
26722 return;
26723 }
26724
26725 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26726 beforeRenderCallbacks(r, true, requestTime);
26727 var startTime = performanceNow();
26728 r.render(r.renderOptions);
26729 var endTime = r.lastDrawTime = performanceNow();
26730
26731 if (r.averageRedrawTime === undefined) {
26732 r.averageRedrawTime = endTime - startTime;
26733 }
26734
26735 if (r.redrawCount === undefined) {
26736 r.redrawCount = 0;
26737 }
26738
26739 r.redrawCount++;
26740
26741 if (r.redrawTotalTime === undefined) {
26742 r.redrawTotalTime = 0;
26743 }
26744
26745 var duration = endTime - startTime;
26746 r.redrawTotalTime += duration;
26747 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26748
26749 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26750 r.requestedFrame = false;
26751 } else {
26752 beforeRenderCallbacks(r, false, requestTime);
26753 }
26754
26755 r.skipFrame = false;
26756 requestAnimationFrame(renderFn);
26757 };
26758
26759 requestAnimationFrame(renderFn);
26760};
26761
26762var BaseRenderer = function BaseRenderer(options) {
26763 this.init(options);
26764};
26765
26766var BR = BaseRenderer;
26767var BRp$f = BR.prototype;
26768BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26769
26770BRp$f.init = function (options) {
26771 var r = this;
26772 r.options = options;
26773 r.cy = options.cy;
26774 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26775
26776 if (window$1) {
26777 var document = window$1.document;
26778 var head = document.head;
26779 var stylesheetId = '__________cytoscape_stylesheet';
26780 var className = '__________cytoscape_container';
26781 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26782
26783 if (ctr.className.indexOf(className) < 0) {
26784 ctr.className = (ctr.className || '') + ' ' + className;
26785 }
26786
26787 if (!stylesheetAlreadyExists) {
26788 var stylesheet = document.createElement('style');
26789 stylesheet.id = stylesheetId;
26790 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26791 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26792 }
26793
26794 var computedStyle = window$1.getComputedStyle(ctr);
26795 var position = computedStyle.getPropertyValue('position');
26796
26797 if (position === 'static') {
26798 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26799 }
26800 }
26801
26802 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26803
26804 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
26805
26806 r.hoverData = {
26807 down: null,
26808 last: null,
26809 downTime: null,
26810 triggerMode: null,
26811 dragging: false,
26812 initialPan: [null, null],
26813 capture: false
26814 };
26815 r.dragData = {
26816 possibleDragElements: []
26817 };
26818 r.touchData = {
26819 start: null,
26820 capture: false,
26821 // These 3 fields related to tap, taphold events
26822 startPosition: [null, null, null, null, null, null],
26823 singleTouchStartTime: null,
26824 singleTouchMoved: true,
26825 now: [null, null, null, null, null, null],
26826 earlier: [null, null, null, null, null, null]
26827 };
26828 r.redraws = 0;
26829 r.showFps = options.showFps;
26830 r.debug = options.debug;
26831 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
26832 r.textureOnViewport = options.textureOnViewport;
26833 r.wheelSensitivity = options.wheelSensitivity;
26834 r.motionBlurEnabled = options.motionBlur; // on by default
26835
26836 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
26837 r.motionBlur = options.motionBlur; // for initial kick off
26838
26839 r.motionBlurOpacity = options.motionBlurOpacity;
26840 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
26841 r.motionBlurPxRatio = 1;
26842 r.mbPxRBlurry = 1; //0.8;
26843
26844 r.minMbLowQualFrames = 4;
26845 r.fullQualityMb = false;
26846 r.clearedForMotionBlur = [];
26847 r.desktopTapThreshold = options.desktopTapThreshold;
26848 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
26849 r.touchTapThreshold = options.touchTapThreshold;
26850 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
26851 r.tapholdDuration = 500;
26852 r.bindings = [];
26853 r.beforeRenderCallbacks = [];
26854 r.beforeRenderPriorities = {
26855 // higher priority execs before lower one
26856 animations: 400,
26857 eleCalcs: 300,
26858 eleTxrDeq: 200,
26859 lyrTxrDeq: 150,
26860 lyrTxrSkip: 100
26861 };
26862 r.registerNodeShapes();
26863 r.registerArrowShapes();
26864 r.registerCalculationListeners();
26865};
26866
26867BRp$f.notify = function (eventName, eles) {
26868 var r = this;
26869 var cy = r.cy; // the renderer can't be notified after it's destroyed
26870
26871 if (this.destroyed) {
26872 return;
26873 }
26874
26875 if (eventName === 'init') {
26876 r.load();
26877 return;
26878 }
26879
26880 if (eventName === 'destroy') {
26881 r.destroy();
26882 return;
26883 }
26884
26885 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
26886 r.invalidateCachedZSortedEles();
26887 }
26888
26889 if (eventName === 'viewport') {
26890 r.redrawHint('select', true);
26891 }
26892
26893 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
26894 r.invalidateContainerClientCoordsCache();
26895 r.matchCanvasSize(r.container);
26896 }
26897
26898 r.redrawHint('eles', true);
26899 r.redrawHint('drag', true);
26900 this.startRenderLoop();
26901 this.redraw();
26902};
26903
26904BRp$f.destroy = function () {
26905 var r = this;
26906 r.destroyed = true;
26907 r.cy.stopAnimationLoop();
26908
26909 for (var i = 0; i < r.bindings.length; i++) {
26910 var binding = r.bindings[i];
26911 var b = binding;
26912 var tgt = b.target;
26913 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
26914 }
26915
26916 r.bindings = [];
26917 r.beforeRenderCallbacks = [];
26918 r.onUpdateEleCalcsFns = [];
26919
26920 if (r.removeObserver) {
26921 r.removeObserver.disconnect();
26922 }
26923
26924 if (r.styleObserver) {
26925 r.styleObserver.disconnect();
26926 }
26927
26928 if (r.resizeObserver) {
26929 r.resizeObserver.disconnect();
26930 }
26931
26932 if (r.labelCalcDiv) {
26933 try {
26934 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
26935 } catch (e) {// ie10 issue #1014
26936 }
26937 }
26938};
26939
26940BRp$f.isHeadless = function () {
26941 return false;
26942};
26943
26944[BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
26945 extend(BRp$f, props);
26946});
26947
26948var fullFpsTime = 1000 / 60; // assume 60 frames per second
26949
26950var defs = {
26951 setupDequeueing: function setupDequeueing(opts) {
26952 return function setupDequeueingImpl() {
26953 var self = this;
26954 var r = this.renderer;
26955
26956 if (self.dequeueingSetup) {
26957 return;
26958 } else {
26959 self.dequeueingSetup = true;
26960 }
26961
26962 var queueRedraw = util(function () {
26963 r.redrawHint('eles', true);
26964 r.redrawHint('drag', true);
26965 r.redraw();
26966 }, opts.deqRedrawThreshold);
26967
26968 var dequeue = function dequeue(willDraw, frameStartTime) {
26969 var startTime = performanceNow();
26970 var avgRenderTime = r.averageRedrawTime;
26971 var renderTime = r.lastRedrawTime;
26972 var deqd = [];
26973 var extent = r.cy.extent();
26974 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
26975 // queue won't automatically be flushed before dequeueing starts
26976
26977 if (!willDraw) {
26978 r.flushRenderedStyleQueue();
26979 }
26980
26981 while (true) {
26982 // eslint-disable-line no-constant-condition
26983 var now = performanceNow();
26984 var duration = now - startTime;
26985 var frameDuration = now - frameStartTime;
26986
26987 if (renderTime < fullFpsTime) {
26988 // if we're rendering faster than the ideal fps, then do dequeueing
26989 // during all of the remaining frame time
26990 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
26991
26992 if (frameDuration >= opts.deqFastCost * timeAvailable) {
26993 break;
26994 }
26995 } else {
26996 if (willDraw) {
26997 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
26998 break;
26999 }
27000 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
27001 break;
27002 }
27003 }
27004
27005 var thisDeqd = opts.deq(self, pixelRatio, extent);
27006
27007 if (thisDeqd.length > 0) {
27008 for (var i = 0; i < thisDeqd.length; i++) {
27009 deqd.push(thisDeqd[i]);
27010 }
27011 } else {
27012 break;
27013 }
27014 } // callbacks on dequeue
27015
27016
27017 if (deqd.length > 0) {
27018 opts.onDeqd(self, deqd);
27019
27020 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27021 queueRedraw();
27022 }
27023 }
27024 };
27025
27026 var priority = opts.priority || noop;
27027 r.beforeRender(dequeue, priority(self));
27028 };
27029 }
27030};
27031
27032// Uses keys so elements may share the same cache.
27033
27034var ElementTextureCacheLookup =
27035/*#__PURE__*/
27036function () {
27037 function ElementTextureCacheLookup(getKey) {
27038 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27039
27040 _classCallCheck(this, ElementTextureCacheLookup);
27041
27042 this.idsByKey = new Map$1();
27043 this.keyForId = new Map$1();
27044 this.cachesByLvl = new Map$1();
27045 this.lvls = [];
27046 this.getKey = getKey;
27047 this.doesEleInvalidateKey = doesEleInvalidateKey;
27048 }
27049
27050 _createClass(ElementTextureCacheLookup, [{
27051 key: "getIdsFor",
27052 value: function getIdsFor(key) {
27053 if (key == null) {
27054 error("Can not get id list for null key");
27055 }
27056
27057 var idsByKey = this.idsByKey;
27058 var ids = this.idsByKey.get(key);
27059
27060 if (!ids) {
27061 ids = new Set$1();
27062 idsByKey.set(key, ids);
27063 }
27064
27065 return ids;
27066 }
27067 }, {
27068 key: "addIdForKey",
27069 value: function addIdForKey(key, id) {
27070 if (key != null) {
27071 this.getIdsFor(key).add(id);
27072 }
27073 }
27074 }, {
27075 key: "deleteIdForKey",
27076 value: function deleteIdForKey(key, id) {
27077 if (key != null) {
27078 this.getIdsFor(key)["delete"](id);
27079 }
27080 }
27081 }, {
27082 key: "getNumberOfIdsForKey",
27083 value: function getNumberOfIdsForKey(key) {
27084 if (key == null) {
27085 return 0;
27086 } else {
27087 return this.getIdsFor(key).size;
27088 }
27089 }
27090 }, {
27091 key: "updateKeyMappingFor",
27092 value: function updateKeyMappingFor(ele) {
27093 var id = ele.id();
27094 var prevKey = this.keyForId.get(id);
27095 var currKey = this.getKey(ele);
27096 this.deleteIdForKey(prevKey, id);
27097 this.addIdForKey(currKey, id);
27098 this.keyForId.set(id, currKey);
27099 }
27100 }, {
27101 key: "deleteKeyMappingFor",
27102 value: function deleteKeyMappingFor(ele) {
27103 var id = ele.id();
27104 var prevKey = this.keyForId.get(id);
27105 this.deleteIdForKey(prevKey, id);
27106 this.keyForId["delete"](id);
27107 }
27108 }, {
27109 key: "keyHasChangedFor",
27110 value: function keyHasChangedFor(ele) {
27111 var id = ele.id();
27112 var prevKey = this.keyForId.get(id);
27113 var newKey = this.getKey(ele);
27114 return prevKey !== newKey;
27115 }
27116 }, {
27117 key: "isInvalid",
27118 value: function isInvalid(ele) {
27119 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27120 }
27121 }, {
27122 key: "getCachesAt",
27123 value: function getCachesAt(lvl) {
27124 var cachesByLvl = this.cachesByLvl,
27125 lvls = this.lvls;
27126 var caches = cachesByLvl.get(lvl);
27127
27128 if (!caches) {
27129 caches = new Map$1();
27130 cachesByLvl.set(lvl, caches);
27131 lvls.push(lvl);
27132 }
27133
27134 return caches;
27135 }
27136 }, {
27137 key: "getCache",
27138 value: function getCache(key, lvl) {
27139 return this.getCachesAt(lvl).get(key);
27140 }
27141 }, {
27142 key: "get",
27143 value: function get(ele, lvl) {
27144 var key = this.getKey(ele);
27145 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27146
27147 if (cache != null) {
27148 this.updateKeyMappingFor(ele);
27149 }
27150
27151 return cache;
27152 }
27153 }, {
27154 key: "getForCachedKey",
27155 value: function getForCachedKey(ele, lvl) {
27156 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27157
27158 var cache = this.getCache(key, lvl);
27159 return cache;
27160 }
27161 }, {
27162 key: "hasCache",
27163 value: function hasCache(key, lvl) {
27164 return this.getCachesAt(lvl).has(key);
27165 }
27166 }, {
27167 key: "has",
27168 value: function has(ele, lvl) {
27169 var key = this.getKey(ele);
27170 return this.hasCache(key, lvl);
27171 }
27172 }, {
27173 key: "setCache",
27174 value: function setCache(key, lvl, cache) {
27175 cache.key = key;
27176 this.getCachesAt(lvl).set(key, cache);
27177 }
27178 }, {
27179 key: "set",
27180 value: function set(ele, lvl, cache) {
27181 var key = this.getKey(ele);
27182 this.setCache(key, lvl, cache);
27183 this.updateKeyMappingFor(ele);
27184 }
27185 }, {
27186 key: "deleteCache",
27187 value: function deleteCache(key, lvl) {
27188 this.getCachesAt(lvl)["delete"](key);
27189 }
27190 }, {
27191 key: "delete",
27192 value: function _delete(ele, lvl) {
27193 var key = this.getKey(ele);
27194 this.deleteCache(key, lvl);
27195 }
27196 }, {
27197 key: "invalidateKey",
27198 value: function invalidateKey(key) {
27199 var _this = this;
27200
27201 this.lvls.forEach(function (lvl) {
27202 return _this.deleteCache(key, lvl);
27203 });
27204 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27205
27206 }, {
27207 key: "invalidate",
27208 value: function invalidate(ele) {
27209 var id = ele.id();
27210 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27211
27212 this.deleteKeyMappingFor(ele);
27213 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27214
27215 if (entireKeyInvalidated) {
27216 // clear mapping for current key
27217 this.invalidateKey(key);
27218 }
27219
27220 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27221 }
27222 }]);
27223
27224 return ElementTextureCacheLookup;
27225}();
27226
27227var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27228
27229var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27230
27231var minLvl = -4; // when scaling smaller than that we don't need to re-render
27232
27233var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27234
27235var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27236
27237var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27238
27239var defTxrWidth = 1024; // default/minimum texture width
27240
27241var maxTxrW = 1024; // the maximum width of a texture
27242
27243var maxTxrH = 1024; // the maximum height of a texture
27244
27245var minUtility = 0.2; // if usage of texture is less than this, it is retired
27246
27247var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27248
27249var maxFullnessChecks = 10; // dequeued after this many checks
27250
27251var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27252
27253var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27254
27255var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27256
27257var deqFastCost = 0.9; // % of frame time to be used when >60fps
27258
27259var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27260
27261var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27262
27263var getTxrReasons = {
27264 dequeue: 'dequeue',
27265 downscale: 'downscale',
27266 highQuality: 'highQuality'
27267};
27268var initDefaults = defaults({
27269 getKey: null,
27270 doesEleInvalidateKey: falsify,
27271 drawElement: null,
27272 getBoundingBox: null,
27273 getRotationPoint: null,
27274 getRotationOffset: null,
27275 isVisible: trueify,
27276 allowEdgeTxrCaching: true,
27277 allowParentTxrCaching: true
27278});
27279
27280var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27281 var self = this;
27282 self.renderer = renderer;
27283 self.onDequeues = [];
27284 var opts = initDefaults(initOptions);
27285 extend(self, opts);
27286 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27287 self.setupDequeueing();
27288};
27289
27290var ETCp = ElementTextureCache.prototype;
27291ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27292
27293ETCp.getTextureQueue = function (txrH) {
27294 var self = this;
27295 self.eleImgCaches = self.eleImgCaches || {};
27296 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27297}; // the list of usused textures which can be recycled (in use in texture queue)
27298
27299
27300ETCp.getRetiredTextureQueue = function (txrH) {
27301 var self = this;
27302 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27303 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27304 return rtxtrQ;
27305}; // queue of element draw requests at different scale levels
27306
27307
27308ETCp.getElementQueue = function () {
27309 var self = this;
27310 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
27311 return b.reqs - a.reqs;
27312 });
27313 return q;
27314}; // queue of element draw requests at different scale levels (element id lookup)
27315
27316
27317ETCp.getElementKeyToQueue = function () {
27318 var self = this;
27319 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27320 return k2q;
27321};
27322
27323ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27324 var self = this;
27325 var r = this.renderer;
27326 var zoom = r.cy.zoom();
27327 var lookup = this.lookup;
27328
27329 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
27330 return null;
27331 }
27332
27333 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27334 return null;
27335 }
27336
27337 if (lvl == null) {
27338 lvl = Math.ceil(log2(zoom * pxRatio));
27339 }
27340
27341 if (lvl < minLvl) {
27342 lvl = minLvl;
27343 } else if (zoom >= maxZoom || lvl > maxLvl) {
27344 return null;
27345 }
27346
27347 var scale = Math.pow(2, lvl);
27348 var eleScaledH = bb.h * scale;
27349 var eleScaledW = bb.w * scale;
27350 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27351
27352 if (!this.isVisible(ele, scaledLabelShown)) {
27353 return null;
27354 }
27355
27356 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
27357
27358 if (eleCache && eleCache.invalidated) {
27359 eleCache.invalidated = false;
27360 eleCache.texture.invalidatedWidth -= eleCache.width;
27361 }
27362
27363 if (eleCache) {
27364 return eleCache;
27365 }
27366
27367 var txrH; // which texture height this ele belongs to
27368
27369 if (eleScaledH <= minTxrH) {
27370 txrH = minTxrH;
27371 } else if (eleScaledH <= txrStepH) {
27372 txrH = txrStepH;
27373 } else {
27374 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27375 }
27376
27377 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27378 return null; // caching large elements is not efficient
27379 }
27380
27381 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
27382
27383 var txr = txrQ[txrQ.length - 2];
27384
27385 var addNewTxr = function addNewTxr() {
27386 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27387 }; // try the last one if there is no second last one
27388
27389
27390 if (!txr) {
27391 txr = txrQ[txrQ.length - 1];
27392 } // if the last one doesn't exist, we need a first one
27393
27394
27395 if (!txr) {
27396 txr = addNewTxr();
27397 } // if there's no room in the current texture, we need a new one
27398
27399
27400 if (txr.width - txr.usedWidth < eleScaledW) {
27401 txr = addNewTxr();
27402 }
27403
27404 var scalableFrom = function scalableFrom(otherCache) {
27405 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27406 };
27407
27408 var deqing = reason && reason === getTxrReasons.dequeue;
27409 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27410 var downscaleReq = reason && reason === getTxrReasons.downscale;
27411 var higherCache; // the nearest cache with a higher level
27412
27413 for (var l = lvl + 1; l <= maxLvl; l++) {
27414 var c = lookup.get(ele, l);
27415
27416 if (c) {
27417 higherCache = c;
27418 break;
27419 }
27420 }
27421
27422 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27423
27424 var downscale = function downscale() {
27425 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27426 }; // reset ele area in texture
27427
27428
27429 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27430 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27431
27432 if (scalableFrom(oneUpCache)) {
27433 // then we can relatively cheaply rescale the existing image w/o rerendering
27434 downscale();
27435 } else if (scalableFrom(higherCache)) {
27436 // then use the higher cache for now and queue the next level down
27437 // to cheaply scale towards the smaller level
27438 if (highQualityReq) {
27439 for (var _l = higherCache.level; _l > lvl; _l--) {
27440 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27441 }
27442
27443 downscale();
27444 } else {
27445 self.queueElement(ele, higherCache.level - 1);
27446 return higherCache;
27447 }
27448 } else {
27449 var lowerCache; // the nearest cache with a lower level
27450
27451 if (!deqing && !highQualityReq && !downscaleReq) {
27452 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
27453 var _c = lookup.get(ele, _l2);
27454
27455 if (_c) {
27456 lowerCache = _c;
27457 break;
27458 }
27459 }
27460 }
27461
27462 if (scalableFrom(lowerCache)) {
27463 // then use the lower quality cache for now and queue the better one for later
27464 self.queueElement(ele, lvl);
27465 return lowerCache;
27466 }
27467
27468 txr.context.translate(txr.usedWidth, 0);
27469 txr.context.scale(scale, scale);
27470 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27471 txr.context.scale(1 / scale, 1 / scale);
27472 txr.context.translate(-txr.usedWidth, 0);
27473 }
27474
27475 eleCache = {
27476 x: txr.usedWidth,
27477 texture: txr,
27478 level: lvl,
27479 scale: scale,
27480 width: eleScaledW,
27481 height: eleScaledH,
27482 scaledLabelShown: scaledLabelShown
27483 };
27484 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27485 txr.eleCaches.push(eleCache);
27486 lookup.set(ele, lvl, eleCache);
27487 self.checkTextureFullness(txr);
27488 return eleCache;
27489};
27490
27491ETCp.invalidateElements = function (eles) {
27492 for (var i = 0; i < eles.length; i++) {
27493 this.invalidateElement(eles[i]);
27494 }
27495};
27496
27497ETCp.invalidateElement = function (ele) {
27498 var self = this;
27499 var lookup = self.lookup;
27500 var caches = [];
27501 var invalid = lookup.isInvalid(ele);
27502
27503 if (!invalid) {
27504 return; // override the invalidation request if the element key has not changed
27505 }
27506
27507 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
27508 var cache = lookup.getForCachedKey(ele, lvl);
27509
27510 if (cache) {
27511 caches.push(cache);
27512 }
27513 }
27514
27515 var noOtherElesUseCache = lookup.invalidate(ele);
27516
27517 if (noOtherElesUseCache) {
27518 for (var i = 0; i < caches.length; i++) {
27519 var _cache = caches[i];
27520 var txr = _cache.texture; // remove space from the texture it belongs to
27521
27522 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27523
27524 _cache.invalidated = true; // retire the texture if its utility is low
27525
27526 self.checkTextureUtility(txr);
27527 }
27528 } // remove from queue since the old req was for the old state
27529
27530
27531 self.removeFromQueue(ele);
27532};
27533
27534ETCp.checkTextureUtility = function (txr) {
27535 // invalidate all entries in the cache if the cache size is small
27536 if (txr.invalidatedWidth >= minUtility * txr.width) {
27537 this.retireTexture(txr);
27538 }
27539};
27540
27541ETCp.checkTextureFullness = function (txr) {
27542 // if texture has been mostly filled and passed over several times, remove
27543 // it from the queue so we don't need to waste time looking at it to put new things
27544 var self = this;
27545 var txrQ = self.getTextureQueue(txr.height);
27546
27547 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27548 removeFromArray(txrQ, txr);
27549 } else {
27550 txr.fullnessChecks++;
27551 }
27552};
27553
27554ETCp.retireTexture = function (txr) {
27555 var self = this;
27556 var txrH = txr.height;
27557 var txrQ = self.getTextureQueue(txrH);
27558 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27559
27560 removeFromArray(txrQ, txr);
27561 txr.retired = true; // remove the refs from the eles to the caches:
27562
27563 var eleCaches = txr.eleCaches;
27564
27565 for (var i = 0; i < eleCaches.length; i++) {
27566 var eleCache = eleCaches[i];
27567 lookup.deleteCache(eleCache.key, eleCache.level);
27568 }
27569
27570 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27571
27572 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27573 rtxtrQ.push(txr);
27574};
27575
27576ETCp.addTexture = function (txrH, minW) {
27577 var self = this;
27578 var txrQ = self.getTextureQueue(txrH);
27579 var txr = {};
27580 txrQ.push(txr);
27581 txr.eleCaches = [];
27582 txr.height = txrH;
27583 txr.width = Math.max(defTxrWidth, minW);
27584 txr.usedWidth = 0;
27585 txr.invalidatedWidth = 0;
27586 txr.fullnessChecks = 0;
27587 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27588 txr.context = txr.canvas.getContext('2d');
27589 return txr;
27590};
27591
27592ETCp.recycleTexture = function (txrH, minW) {
27593 var self = this;
27594 var txrQ = self.getTextureQueue(txrH);
27595 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27596
27597 for (var i = 0; i < rtxtrQ.length; i++) {
27598 var txr = rtxtrQ[i];
27599
27600 if (txr.width >= minW) {
27601 txr.retired = false;
27602 txr.usedWidth = 0;
27603 txr.invalidatedWidth = 0;
27604 txr.fullnessChecks = 0;
27605 clearArray(txr.eleCaches);
27606 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27607 txr.context.clearRect(0, 0, txr.width, txr.height);
27608 removeFromArray(rtxtrQ, txr);
27609 txrQ.push(txr);
27610 return txr;
27611 }
27612 }
27613};
27614
27615ETCp.queueElement = function (ele, lvl) {
27616 var self = this;
27617 var q = self.getElementQueue();
27618 var k2q = self.getElementKeyToQueue();
27619 var key = this.getKey(ele);
27620 var existingReq = k2q[key];
27621
27622 if (existingReq) {
27623 // use the max lvl b/c in between lvls are cheap to make
27624 existingReq.level = Math.max(existingReq.level, lvl);
27625 existingReq.eles.merge(ele);
27626 existingReq.reqs++;
27627 q.updateItem(existingReq);
27628 } else {
27629 var req = {
27630 eles: ele.spawn().merge(ele),
27631 level: lvl,
27632 reqs: 1,
27633 key: key
27634 };
27635 q.push(req);
27636 k2q[key] = req;
27637 }
27638};
27639
27640ETCp.dequeue = function (pxRatio
27641/*, extent*/
27642) {
27643 var self = this;
27644 var q = self.getElementQueue();
27645 var k2q = self.getElementKeyToQueue();
27646 var dequeued = [];
27647 var lookup = self.lookup;
27648
27649 for (var i = 0; i < maxDeqSize; i++) {
27650 if (q.size() > 0) {
27651 var req = q.pop();
27652 var key = req.key;
27653 var ele = req.eles[0]; // all eles have the same key
27654
27655 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27656
27657 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27658
27659 if (cacheExists) {
27660 continue;
27661 }
27662
27663 dequeued.push(req);
27664 var bb = self.getBoundingBox(ele);
27665 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27666 } else {
27667 break;
27668 }
27669 }
27670
27671 return dequeued;
27672};
27673
27674ETCp.removeFromQueue = function (ele) {
27675 var self = this;
27676 var q = self.getElementQueue();
27677 var k2q = self.getElementKeyToQueue();
27678 var key = this.getKey(ele);
27679 var req = k2q[key];
27680
27681 if (req != null) {
27682 if (req.eles.length === 1) {
27683 // remove if last ele in the req
27684 // bring to front of queue
27685 req.reqs = MAX_INT;
27686 q.updateItem(req);
27687 q.pop(); // remove from queue
27688
27689 k2q[key] = null; // remove from lookup map
27690 } else {
27691 // otherwise just remove ele from req
27692 req.eles.unmerge(ele);
27693 }
27694 }
27695};
27696
27697ETCp.onDequeue = function (fn) {
27698 this.onDequeues.push(fn);
27699};
27700
27701ETCp.offDequeue = function (fn) {
27702 removeFromArray(this.onDequeues, fn);
27703};
27704
27705ETCp.setupDequeueing = defs.setupDequeueing({
27706 deqRedrawThreshold: deqRedrawThreshold,
27707 deqCost: deqCost,
27708 deqAvgCost: deqAvgCost,
27709 deqNoDrawCost: deqNoDrawCost,
27710 deqFastCost: deqFastCost,
27711 deq: function deq(self, pxRatio, extent) {
27712 return self.dequeue(pxRatio, extent);
27713 },
27714 onDeqd: function onDeqd(self, deqd) {
27715 for (var i = 0; i < self.onDequeues.length; i++) {
27716 var fn = self.onDequeues[i];
27717 fn(deqd);
27718 }
27719 },
27720 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27721 for (var i = 0; i < deqd.length; i++) {
27722 var eles = deqd[i].eles;
27723
27724 for (var j = 0; j < eles.length; j++) {
27725 var bb = eles[j].boundingBox();
27726
27727 if (boundingBoxesIntersect(bb, extent)) {
27728 return true;
27729 }
27730 }
27731 }
27732
27733 return false;
27734 },
27735 priority: function priority(self) {
27736 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27737 }
27738});
27739
27740var defNumLayers = 1; // default number of layers to use
27741
27742var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27743
27744var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
27745
27746var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
27747
27748var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27749
27750var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27751
27752var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27753
27754var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27755
27756var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27757
27758var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27759
27760var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27761
27762var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27763
27764var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27765
27766var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27767// var log = function(){ console.log.apply( console, arguments ); };
27768
27769var LayeredTextureCache = function LayeredTextureCache(renderer) {
27770 var self = this;
27771 var r = self.renderer = renderer;
27772 var cy = r.cy;
27773 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27774
27775 self.firstGet = true;
27776 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27777 self.skipping = false;
27778 self.eleTxrDeqs = cy.collection();
27779 self.scheduleElementRefinement = util(function () {
27780 self.refineElementTextures(self.eleTxrDeqs);
27781 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27782 }, refineEleDebounceTime);
27783 r.beforeRender(function (willDraw, now) {
27784 if (now - self.lastInvalidationTime <= invalidThreshold) {
27785 self.skipping = true;
27786 } else {
27787 self.skipping = false;
27788 }
27789 }, r.beforeRenderPriorities.lyrTxrSkip);
27790
27791 var qSort = function qSort(a, b) {
27792 return b.reqs - a.reqs;
27793 };
27794
27795 self.layersQueue = new Heap(qSort);
27796 self.setupDequeueing();
27797};
27798
27799var LTCp = LayeredTextureCache.prototype;
27800var layerIdPool = 0;
27801var MAX_INT$1 = Math.pow(2, 53) - 1;
27802
27803LTCp.makeLayer = function (bb, lvl) {
27804 var scale = Math.pow(2, lvl);
27805 var w = Math.ceil(bb.w * scale);
27806 var h = Math.ceil(bb.h * scale);
27807 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27808 var layer = {
27809 id: layerIdPool = ++layerIdPool % MAX_INT$1,
27810 bb: bb,
27811 level: lvl,
27812 width: w,
27813 height: h,
27814 canvas: canvas,
27815 context: canvas.getContext('2d'),
27816 eles: [],
27817 elesQueue: [],
27818 reqs: 0
27819 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27820
27821 var cxt = layer.context;
27822 var dx = -layer.bb.x1;
27823 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
27824
27825 cxt.scale(scale, scale);
27826 cxt.translate(dx, dy);
27827 return layer;
27828};
27829
27830LTCp.getLayers = function (eles, pxRatio, lvl) {
27831 var self = this;
27832 var r = self.renderer;
27833 var cy = r.cy;
27834 var zoom = cy.zoom();
27835 var firstGet = self.firstGet;
27836 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
27837 //log eles.map(function(ele){ return ele.id() }) );
27838
27839 if (lvl == null) {
27840 lvl = Math.ceil(log2(zoom * pxRatio));
27841
27842 if (lvl < minLvl$1) {
27843 lvl = minLvl$1;
27844 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27845 return null;
27846 }
27847 }
27848
27849 self.validateLayersElesOrdering(lvl, eles);
27850 var layersByLvl = self.layersByLevel;
27851 var scale = Math.pow(2, lvl);
27852 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
27853 var bb;
27854 var lvlComplete = self.levelIsComplete(lvl, eles);
27855 var tmpLayers;
27856
27857 var checkTempLevels = function checkTempLevels() {
27858 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
27859 self.validateLayersElesOrdering(l, eles);
27860
27861 if (self.levelIsComplete(l, eles)) {
27862 tmpLayers = layersByLvl[l];
27863 return true;
27864 }
27865 };
27866
27867 var checkLvls = function checkLvls(dir) {
27868 if (tmpLayers) {
27869 return;
27870 }
27871
27872 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
27873 if (canUseAsTmpLvl(l)) {
27874 break;
27875 }
27876 }
27877 };
27878
27879 checkLvls(+1);
27880 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
27881
27882 for (var i = layers.length - 1; i >= 0; i--) {
27883 var layer = layers[i];
27884
27885 if (layer.invalid) {
27886 removeFromArray(layers, layer);
27887 }
27888 }
27889 };
27890
27891 if (!lvlComplete) {
27892 // if the current level is incomplete, then use the closest, best quality layerset temporarily
27893 // and later queue the current layerset so we can get the proper quality level soon
27894 checkTempLevels();
27895 } else {
27896 // log('level complete, using existing layers\n--');
27897 return layers;
27898 }
27899
27900 var getBb = function getBb() {
27901 if (!bb) {
27902 bb = makeBoundingBox();
27903
27904 for (var i = 0; i < eles.length; i++) {
27905 updateBoundingBox(bb, eles[i].boundingBox());
27906 }
27907 }
27908
27909 return bb;
27910 };
27911
27912 var makeLayer = function makeLayer(opts) {
27913 opts = opts || {};
27914 var after = opts.after;
27915 getBb();
27916 var area = bb.w * scale * (bb.h * scale);
27917
27918 if (area > maxLayerArea) {
27919 return null;
27920 }
27921
27922 var layer = self.makeLayer(bb, lvl);
27923
27924 if (after != null) {
27925 var index = layers.indexOf(after) + 1;
27926 layers.splice(index, 0, layer);
27927 } else if (opts.insert === undefined || opts.insert) {
27928 // no after specified => first layer made so put at start
27929 layers.unshift(layer);
27930 } // if( tmpLayers ){
27931 //self.queueLayer( layer );
27932 // }
27933
27934
27935 return layer;
27936 };
27937
27938 if (self.skipping && !firstGet) {
27939 // log('skip layers');
27940 return null;
27941 } // log('do layers');
27942
27943
27944 var layer = null;
27945 var maxElesPerLayer = eles.length / defNumLayers;
27946 var allowLazyQueueing = !firstGet;
27947
27948 for (var i = 0; i < eles.length; i++) {
27949 var ele = eles[i];
27950 var rs = ele._private.rscratch;
27951 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
27952
27953 var existingLayer = caches[lvl];
27954
27955 if (existingLayer) {
27956 // reuse layer for later eles
27957 // log('reuse layer for', ele.id());
27958 layer = existingLayer;
27959 continue;
27960 }
27961
27962 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
27963 // log('make new layer for ele %s', ele.id());
27964 layer = makeLayer({
27965 insert: true,
27966 after: layer
27967 }); // if now layer can be built then we can't use layers at this level
27968
27969 if (!layer) {
27970 return null;
27971 } // log('new layer with id %s', layer.id);
27972
27973 }
27974
27975 if (tmpLayers || allowLazyQueueing) {
27976 // log('queue ele %s in layer %s', ele.id(), layer.id);
27977 self.queueLayer(layer, ele);
27978 } else {
27979 // log('draw ele %s in layer %s', ele.id(), layer.id);
27980 self.drawEleInLayer(layer, ele, lvl, pxRatio);
27981 }
27982
27983 layer.eles.push(ele);
27984 caches[lvl] = layer;
27985 } // log('--');
27986
27987
27988 if (tmpLayers) {
27989 // then we only queued the current layerset and can't draw it yet
27990 return tmpLayers;
27991 }
27992
27993 if (allowLazyQueueing) {
27994 // log('lazy queue level', lvl);
27995 return null;
27996 }
27997
27998 return layers;
27999}; // a layer may want to use an ele cache of a higher level to avoid blurriness
28000// so the layer level might not equal the ele level
28001
28002
28003LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
28004 return lvl;
28005};
28006
28007LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
28008 var self = this;
28009 var r = this.renderer;
28010 var context = layer.context;
28011 var bb = ele.boundingBox();
28012
28013 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28014 return;
28015 }
28016
28017 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
28018
28019 {
28020 r.setImgSmoothing(context, false);
28021 }
28022
28023 {
28024 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
28025 }
28026
28027 {
28028 r.setImgSmoothing(context, true);
28029 }
28030};
28031
28032LTCp.levelIsComplete = function (lvl, eles) {
28033 var self = this;
28034 var layers = self.layersByLevel[lvl];
28035
28036 if (!layers || layers.length === 0) {
28037 return false;
28038 }
28039
28040 var numElesInLayers = 0;
28041
28042 for (var i = 0; i < layers.length; i++) {
28043 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28044
28045 if (layer.reqs > 0) {
28046 return false;
28047 } // if the layer is invalid, the level is not complete
28048
28049
28050 if (layer.invalid) {
28051 return false;
28052 }
28053
28054 numElesInLayers += layer.eles.length;
28055 } // we should have exactly the number of eles passed in to be complete
28056
28057
28058 if (numElesInLayers !== eles.length) {
28059 return false;
28060 }
28061
28062 return true;
28063};
28064
28065LTCp.validateLayersElesOrdering = function (lvl, eles) {
28066 var layers = this.layersByLevel[lvl];
28067
28068 if (!layers) {
28069 return;
28070 } // if in a layer the eles are not in the same order, then the layer is invalid
28071 // (i.e. there is an ele in between the eles in the layer)
28072
28073
28074 for (var i = 0; i < layers.length; i++) {
28075 var layer = layers[i];
28076 var offset = -1; // find the offset
28077
28078 for (var j = 0; j < eles.length; j++) {
28079 if (layer.eles[0] === eles[j]) {
28080 offset = j;
28081 break;
28082 }
28083 }
28084
28085 if (offset < 0) {
28086 // then the layer has nonexistant elements and is invalid
28087 this.invalidateLayer(layer);
28088 continue;
28089 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28090
28091
28092 var o = offset;
28093
28094 for (var j = 0; j < layer.eles.length; j++) {
28095 if (layer.eles[j] !== eles[o + j]) {
28096 // log('invalidate based on ordering', layer.id);
28097 this.invalidateLayer(layer);
28098 break;
28099 }
28100 }
28101 }
28102};
28103
28104LTCp.updateElementsInLayers = function (eles, update) {
28105 var self = this;
28106 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28107 // layer itself along the way
28108
28109 for (var i = 0; i < eles.length; i++) {
28110 var req = isEles ? null : eles[i];
28111 var ele = isEles ? eles[i] : eles[i].ele;
28112 var rs = ele._private.rscratch;
28113 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28114
28115 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28116 var layer = caches[l];
28117
28118 if (!layer) {
28119 continue;
28120 } // if update is a request from the ele cache, then it affects only
28121 // the matching level
28122
28123
28124 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28125 continue;
28126 }
28127
28128 update(layer, ele, req);
28129 }
28130 }
28131};
28132
28133LTCp.haveLayers = function () {
28134 var self = this;
28135 var haveLayers = false;
28136
28137 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28138 var layers = self.layersByLevel[l];
28139
28140 if (layers && layers.length > 0) {
28141 haveLayers = true;
28142 break;
28143 }
28144 }
28145
28146 return haveLayers;
28147};
28148
28149LTCp.invalidateElements = function (eles) {
28150 var self = this;
28151
28152 if (eles.length === 0) {
28153 return;
28154 }
28155
28156 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28157
28158 if (eles.length === 0 || !self.haveLayers()) {
28159 return;
28160 }
28161
28162 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28163 self.invalidateLayer(layer);
28164 });
28165};
28166
28167LTCp.invalidateLayer = function (layer) {
28168 // log('update invalidate layer time');
28169 this.lastInvalidationTime = performanceNow();
28170
28171 if (layer.invalid) {
28172 return;
28173 } // save cycles
28174
28175
28176 var lvl = layer.level;
28177 var eles = layer.eles;
28178 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28179
28180 removeFromArray(layers, layer); // layer.eles = [];
28181
28182 layer.elesQueue = [];
28183 layer.invalid = true;
28184
28185 if (layer.replacement) {
28186 layer.replacement.invalid = true;
28187 }
28188
28189 for (var i = 0; i < eles.length; i++) {
28190 var caches = eles[i]._private.rscratch.imgLayerCaches;
28191
28192 if (caches) {
28193 caches[lvl] = null;
28194 }
28195 }
28196};
28197
28198LTCp.refineElementTextures = function (eles) {
28199 var self = this; // log('refine', eles.length);
28200
28201 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28202 var rLyr = layer.replacement;
28203
28204 if (!rLyr) {
28205 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28206 rLyr.replaces = layer;
28207 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28208 }
28209
28210 if (!rLyr.reqs) {
28211 for (var i = 0; i < rLyr.eles.length; i++) {
28212 self.queueLayer(rLyr, rLyr.eles[i]);
28213 } // log('queue replacement layer refinement', rLyr.id);
28214
28215 }
28216 });
28217};
28218
28219LTCp.enqueueElementRefinement = function (ele) {
28220
28221 this.eleTxrDeqs.merge(ele);
28222 this.scheduleElementRefinement();
28223};
28224
28225LTCp.queueLayer = function (layer, ele) {
28226 var self = this;
28227 var q = self.layersQueue;
28228 var elesQ = layer.elesQueue;
28229 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28230
28231 if (layer.replacement) {
28232 return;
28233 }
28234
28235 if (ele) {
28236 if (hasId[ele.id()]) {
28237 return;
28238 }
28239
28240 elesQ.push(ele);
28241 hasId[ele.id()] = true;
28242 }
28243
28244 if (layer.reqs) {
28245 layer.reqs++;
28246 q.updateItem(layer);
28247 } else {
28248 layer.reqs = 1;
28249 q.push(layer);
28250 }
28251};
28252
28253LTCp.dequeue = function (pxRatio) {
28254 var self = this;
28255 var q = self.layersQueue;
28256 var deqd = [];
28257 var eleDeqs = 0;
28258
28259 while (eleDeqs < maxDeqSize$1) {
28260 if (q.size() === 0) {
28261 break;
28262 }
28263
28264 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28265
28266 if (layer.replacement) {
28267 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28268 q.pop();
28269 continue;
28270 } // if this is a replacement layer that has been superceded, then forget it
28271
28272
28273 if (layer.replaces && layer !== layer.replaces.replacement) {
28274 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28275 q.pop();
28276 continue;
28277 }
28278
28279 if (layer.invalid) {
28280 // log('replacement layer %s is invalid; dequeued', layer.id);
28281 q.pop();
28282 continue;
28283 }
28284
28285 var ele = layer.elesQueue.shift();
28286
28287 if (ele) {
28288 // log('dequeue layer %s', layer.id);
28289 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28290 eleDeqs++;
28291 }
28292
28293 if (deqd.length === 0) {
28294 // we need only one entry in deqd to queue redrawing etc
28295 deqd.push(true);
28296 } // if the layer has all its eles done, then remove from the queue
28297
28298
28299 if (layer.elesQueue.length === 0) {
28300 q.pop();
28301 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28302 // when a replacement layer is dequeued, it replaces the old layer in the level
28303
28304 if (layer.replaces) {
28305 self.applyLayerReplacement(layer);
28306 }
28307
28308 self.requestRedraw();
28309 }
28310 }
28311
28312 return deqd;
28313};
28314
28315LTCp.applyLayerReplacement = function (layer) {
28316 var self = this;
28317 var layersInLevel = self.layersByLevel[layer.level];
28318 var replaced = layer.replaces;
28319 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28320 // refs would be a mistake (i.e. overwriting the true active layer)
28321
28322 if (index < 0 || replaced.invalid) {
28323 // log('replacement layer would have no effect', layer.id);
28324 return;
28325 }
28326
28327 layersInLevel[index] = layer; // replace level ref
28328 // replace refs in eles
28329
28330 for (var i = 0; i < layer.eles.length; i++) {
28331 var _p = layer.eles[i]._private;
28332 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28333
28334 if (cache) {
28335 cache[layer.level] = layer;
28336 }
28337 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
28338
28339
28340 self.requestRedraw();
28341};
28342
28343LTCp.requestRedraw = util(function () {
28344 var r = this.renderer;
28345 r.redrawHint('eles', true);
28346 r.redrawHint('drag', true);
28347 r.redraw();
28348}, 100);
28349LTCp.setupDequeueing = defs.setupDequeueing({
28350 deqRedrawThreshold: deqRedrawThreshold$1,
28351 deqCost: deqCost$1,
28352 deqAvgCost: deqAvgCost$1,
28353 deqNoDrawCost: deqNoDrawCost$1,
28354 deqFastCost: deqFastCost$1,
28355 deq: function deq(self, pxRatio) {
28356 return self.dequeue(pxRatio);
28357 },
28358 onDeqd: noop,
28359 shouldRedraw: trueify,
28360 priority: function priority(self) {
28361 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28362 }
28363});
28364
28365var CRp = {};
28366var impl;
28367
28368function polygon(context, points) {
28369 for (var i = 0; i < points.length; i++) {
28370 var pt = points[i];
28371 context.lineTo(pt.x, pt.y);
28372 }
28373}
28374
28375function triangleBackcurve(context, points, controlPoint) {
28376 var firstPt;
28377
28378 for (var i = 0; i < points.length; i++) {
28379 var pt = points[i];
28380
28381 if (i === 0) {
28382 firstPt = pt;
28383 }
28384
28385 context.lineTo(pt.x, pt.y);
28386 }
28387
28388 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28389}
28390
28391function triangleTee(context, trianglePoints, teePoints) {
28392 if (context.beginPath) {
28393 context.beginPath();
28394 }
28395
28396 var triPts = trianglePoints;
28397
28398 for (var i = 0; i < triPts.length; i++) {
28399 var pt = triPts[i];
28400 context.lineTo(pt.x, pt.y);
28401 }
28402
28403 var teePts = teePoints;
28404 var firstTeePt = teePoints[0];
28405 context.moveTo(firstTeePt.x, firstTeePt.y);
28406
28407 for (var i = 1; i < teePts.length; i++) {
28408 var pt = teePts[i];
28409 context.lineTo(pt.x, pt.y);
28410 }
28411
28412 if (context.closePath) {
28413 context.closePath();
28414 }
28415}
28416
28417function circleTriangle(context, trianglePoints, rx, ry, r) {
28418 if (context.beginPath) {
28419 context.beginPath();
28420 }
28421
28422 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28423 var triPts = trianglePoints;
28424 var firstTrPt = triPts[0];
28425 context.moveTo(firstTrPt.x, firstTrPt.y);
28426
28427 for (var i = 0; i < triPts.length; i++) {
28428 var pt = triPts[i];
28429 context.lineTo(pt.x, pt.y);
28430 }
28431
28432 if (context.closePath) {
28433 context.closePath();
28434 }
28435}
28436
28437function circle(context, rx, ry, r) {
28438 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28439}
28440
28441CRp.arrowShapeImpl = function (name) {
28442 return (impl || (impl = {
28443 'polygon': polygon,
28444 'triangle-backcurve': triangleBackcurve,
28445 'triangle-tee': triangleTee,
28446 'circle-triangle': circleTriangle,
28447 'triangle-cross': triangleTee,
28448 'circle': circle
28449 }))[name];
28450};
28451
28452var CRp$1 = {};
28453
28454CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28455 var r = this;
28456
28457 if (ele.isNode()) {
28458 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28459 } else {
28460 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28461 }
28462};
28463
28464CRp$1.drawElementOverlay = function (context, ele) {
28465 var r = this;
28466
28467 if (ele.isNode()) {
28468 r.drawNodeOverlay(context, ele);
28469 } else {
28470 r.drawEdgeOverlay(context, ele);
28471 }
28472};
28473
28474CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28475 var r = this;
28476 var bb = eleTxrCache.getBoundingBox(ele);
28477
28478 if (bb.w === 0 || bb.h === 0) {
28479 return;
28480 } // ignore zero size case
28481
28482
28483 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28484
28485 if (eleCache != null) {
28486 var opacity = getOpacity(r, ele);
28487
28488 if (opacity === 0) {
28489 return;
28490 }
28491
28492 var theta = getRotation(r, ele);
28493 var x1 = bb.x1,
28494 y1 = bb.y1,
28495 w = bb.w,
28496 h = bb.h;
28497 var x, y, sx, sy, smooth;
28498
28499 if (theta !== 0) {
28500 var rotPt = eleTxrCache.getRotationPoint(ele);
28501 sx = rotPt.x;
28502 sy = rotPt.y;
28503 context.translate(sx, sy);
28504 context.rotate(theta);
28505 smooth = r.getImgSmoothing(context);
28506
28507 if (!smooth) {
28508 r.setImgSmoothing(context, true);
28509 }
28510
28511 var off = eleTxrCache.getRotationOffset(ele);
28512 x = off.x;
28513 y = off.y;
28514 } else {
28515 x = x1;
28516 y = y1;
28517 }
28518
28519 var oldGlobalAlpha;
28520
28521 if (opacity !== 1) {
28522 oldGlobalAlpha = context.globalAlpha;
28523 context.globalAlpha = oldGlobalAlpha * opacity;
28524 }
28525
28526 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28527
28528 if (opacity !== 1) {
28529 context.globalAlpha = oldGlobalAlpha;
28530 }
28531
28532 if (theta !== 0) {
28533 context.rotate(-theta);
28534 context.translate(-sx, -sy);
28535
28536 if (!smooth) {
28537 r.setImgSmoothing(context, false);
28538 }
28539 }
28540 } else {
28541 eleTxrCache.drawElement(context, ele); // direct draw fallback
28542 }
28543};
28544
28545var getZeroRotation = function getZeroRotation() {
28546 return 0;
28547};
28548
28549var getLabelRotation = function getLabelRotation(r, ele) {
28550 return r.getTextAngle(ele, null);
28551};
28552
28553var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28554 return r.getTextAngle(ele, 'source');
28555};
28556
28557var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28558 return r.getTextAngle(ele, 'target');
28559};
28560
28561var getOpacity = function getOpacity(r, ele) {
28562 return ele.effectiveOpacity();
28563};
28564
28565var getTextOpacity = function getTextOpacity(e, ele) {
28566 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28567};
28568
28569CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28570 var r = this;
28571 var _r$data = r.data,
28572 eleTxrCache = _r$data.eleTxrCache,
28573 lblTxrCache = _r$data.lblTxrCache,
28574 slbTxrCache = _r$data.slbTxrCache,
28575 tlbTxrCache = _r$data.tlbTxrCache;
28576 var bb = ele.boundingBox();
28577 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28578
28579 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28580 return;
28581 }
28582
28583 if (!extent || boundingBoxesIntersect(bb, extent)) {
28584 var isEdge = ele.isEdge();
28585
28586 var badLine = ele.element()._private.rscratch.badLine;
28587
28588 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28589
28590 if (!isEdge || !badLine) {
28591 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28592 }
28593
28594 if (isEdge && !badLine) {
28595 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28596 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28597 }
28598
28599 r.drawElementOverlay(context, ele);
28600 }
28601};
28602
28603CRp$1.drawElements = function (context, eles) {
28604 var r = this;
28605
28606 for (var i = 0; i < eles.length; i++) {
28607 var ele = eles[i];
28608 r.drawElement(context, ele);
28609 }
28610};
28611
28612CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
28613 var r = this;
28614
28615 for (var i = 0; i < eles.length; i++) {
28616 var ele = eles[i];
28617 r.drawCachedElement(context, ele, pxRatio, extent);
28618 }
28619};
28620
28621CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
28622 var r = this;
28623
28624 for (var i = 0; i < eles.length; i++) {
28625 var ele = eles[i];
28626
28627 if (!ele.isNode()) {
28628 continue;
28629 }
28630
28631 r.drawCachedElement(context, ele, pxRatio, extent);
28632 }
28633};
28634
28635CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
28636 var r = this;
28637 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28638
28639 if (layers) {
28640 for (var i = 0; i < layers.length; i++) {
28641 var layer = layers[i];
28642 var bb = layer.bb;
28643
28644 if (bb.w === 0 || bb.h === 0) {
28645 continue;
28646 }
28647
28648 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28649 }
28650 } else {
28651 // fall back on plain caching if no layers
28652 r.drawCachedElements(context, eles, pxRatio, extent);
28653 }
28654};
28655
28656/* global Path2D */
28657var CRp$2 = {};
28658
28659CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
28660 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28661 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28662 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28663 var r = this;
28664 var rs = edge._private.rscratch;
28665
28666 if (shouldDrawOpacity && !edge.visible()) {
28667 return;
28668 } // if bezier ctrl pts can not be calculated, then die
28669
28670
28671 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28672 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28673 return;
28674 }
28675
28676 var bb;
28677
28678 if (shiftToOriginWithBb) {
28679 bb = shiftToOriginWithBb;
28680 context.translate(-bb.x1, -bb.y1);
28681 }
28682
28683 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28684 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
28685 var lineStyle = edge.pstyle('line-style').value;
28686 var edgeWidth = edge.pstyle('width').pfValue;
28687 var lineCap = edge.pstyle('line-cap').value;
28688 var effectiveLineOpacity = opacity * lineOpacity; // separate arrow opacity would require arrow-opacity property
28689
28690 var effectiveArrowOpacity = opacity * lineOpacity;
28691
28692 var drawLine = function drawLine() {
28693 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28694 context.lineWidth = edgeWidth;
28695 context.lineCap = lineCap;
28696 r.eleStrokeStyle(context, edge, strokeOpacity);
28697 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28698 context.lineCap = 'butt'; // reset for other drawing functions
28699 };
28700
28701 var drawOverlay = function drawOverlay() {
28702 if (!shouldDrawOverlay) {
28703 return;
28704 }
28705
28706 r.drawEdgeOverlay(context, edge);
28707 };
28708
28709 var drawArrows = function drawArrows() {
28710 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
28711 r.drawArrowheads(context, edge, arrowOpacity);
28712 };
28713
28714 var drawText = function drawText() {
28715 r.drawElementText(context, edge, null, drawLabel);
28716 };
28717
28718 context.lineJoin = 'round';
28719 var ghost = edge.pstyle('ghost').value === 'yes';
28720
28721 if (ghost) {
28722 var gx = edge.pstyle('ghost-offset-x').pfValue;
28723 var gy = edge.pstyle('ghost-offset-y').pfValue;
28724 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28725 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
28726 context.translate(gx, gy);
28727 drawLine(effectiveGhostOpacity);
28728 drawArrows(effectiveGhostOpacity);
28729 context.translate(-gx, -gy);
28730 }
28731
28732 drawLine();
28733 drawArrows();
28734 drawOverlay();
28735 drawText();
28736
28737 if (shiftToOriginWithBb) {
28738 context.translate(bb.x1, bb.y1);
28739 }
28740};
28741
28742CRp$2.drawEdgeOverlay = function (context, edge) {
28743 if (!edge.visible()) {
28744 return;
28745 }
28746
28747 var overlayOpacity = edge.pstyle('overlay-opacity').value;
28748
28749 if (overlayOpacity === 0) {
28750 return;
28751 }
28752
28753 var r = this;
28754 var usePaths = r.usePaths();
28755 var rs = edge._private.rscratch;
28756 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
28757 var overlayWidth = 2 * overlayPadding;
28758 var overlayColor = edge.pstyle('overlay-color').value;
28759 context.lineWidth = overlayWidth;
28760
28761 if (rs.edgeType === 'self' && !usePaths) {
28762 context.lineCap = 'butt';
28763 } else {
28764 context.lineCap = 'round';
28765 }
28766
28767 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
28768 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28769};
28770
28771CRp$2.drawEdgePath = function (edge, context, pts, type) {
28772 var rs = edge._private.rscratch;
28773 var canvasCxt = context;
28774 var path;
28775 var pathCacheHit = false;
28776 var usePaths = this.usePaths();
28777 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28778 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28779
28780 if (usePaths) {
28781 var pathCacheKey = pts.join('$');
28782 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28783
28784 if (keyMatches) {
28785 path = context = rs.pathCache;
28786 pathCacheHit = true;
28787 } else {
28788 path = context = new Path2D();
28789 rs.pathCacheKey = pathCacheKey;
28790 rs.pathCache = path;
28791 }
28792 }
28793
28794 if (canvasCxt.setLineDash) {
28795 // for very outofdate browsers
28796 switch (type) {
28797 case 'dotted':
28798 canvasCxt.setLineDash([1, 1]);
28799 break;
28800
28801 case 'dashed':
28802 canvasCxt.setLineDash(lineDashPattern);
28803 canvasCxt.lineDashOffset = lineDashOffset;
28804 break;
28805
28806 case 'solid':
28807 canvasCxt.setLineDash([]);
28808 break;
28809 }
28810 }
28811
28812 if (!pathCacheHit && !rs.badLine) {
28813 if (context.beginPath) {
28814 context.beginPath();
28815 }
28816
28817 context.moveTo(pts[0], pts[1]);
28818
28819 switch (rs.edgeType) {
28820 case 'bezier':
28821 case 'self':
28822 case 'compound':
28823 case 'multibezier':
28824 for (var i = 2; i + 3 < pts.length; i += 4) {
28825 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
28826 }
28827
28828 break;
28829
28830 case 'straight':
28831 case 'segments':
28832 case 'haystack':
28833 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
28834 context.lineTo(pts[_i], pts[_i + 1]);
28835 }
28836
28837 break;
28838 }
28839 }
28840
28841 context = canvasCxt;
28842
28843 if (usePaths) {
28844 context.stroke(path);
28845 } else {
28846 context.stroke();
28847 } // reset any line dashes
28848
28849
28850 if (context.setLineDash) {
28851 // for very outofdate browsers
28852 context.setLineDash([]);
28853 }
28854};
28855
28856CRp$2.drawArrowheads = function (context, edge, opacity) {
28857 var rs = edge._private.rscratch;
28858 var isHaystack = rs.edgeType === 'haystack';
28859
28860 if (!isHaystack) {
28861 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
28862 }
28863
28864 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
28865 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
28866
28867 if (!isHaystack) {
28868 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
28869 }
28870};
28871
28872CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
28873 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
28874 return;
28875 }
28876
28877 var self = this;
28878 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
28879
28880 if (arrowShape === 'none') {
28881 return;
28882 }
28883
28884 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
28885 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
28886 var edgeWidth = edge.pstyle('width').pfValue;
28887 var edgeOpacity = edge.pstyle('opacity').value;
28888
28889 if (opacity === undefined) {
28890 opacity = edgeOpacity;
28891 }
28892
28893 var gco = context.globalCompositeOperation;
28894
28895 if (opacity !== 1 || arrowFill === 'hollow') {
28896 // then extra clear is needed
28897 context.globalCompositeOperation = 'destination-out';
28898 self.colorFillStyle(context, 255, 255, 255, 1);
28899 self.colorStrokeStyle(context, 255, 255, 255, 1);
28900 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
28901 context.globalCompositeOperation = gco;
28902 } // otherwise, the opaque arrow clears it for free :)
28903
28904
28905 var color = edge.pstyle(prefix + '-arrow-color').value;
28906 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
28907 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28908 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
28909};
28910
28911CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
28912 var r = this;
28913 var usePaths = this.usePaths() && shape !== 'triangle-cross';
28914 var pathCacheHit = false;
28915 var path;
28916 var canvasContext = context;
28917 var translation = {
28918 x: x,
28919 y: y
28920 };
28921 var scale = edge.pstyle('arrow-scale').value;
28922 var size = this.getArrowWidth(edgeWidth, scale);
28923 var shapeImpl = r.arrowShapes[shape];
28924
28925 if (usePaths) {
28926 var cache = r.arrowPathCache = r.arrowPathCache || [];
28927 var key = hashString(shape);
28928 var cachedPath = cache[key];
28929
28930 if (cachedPath != null) {
28931 path = context = cachedPath;
28932 pathCacheHit = true;
28933 } else {
28934 path = context = new Path2D();
28935 cache[key] = path;
28936 }
28937 }
28938
28939 if (!pathCacheHit) {
28940 if (context.beginPath) {
28941 context.beginPath();
28942 }
28943
28944 if (usePaths) {
28945 // store in the path cache with values easily manipulated later
28946 shapeImpl.draw(context, 1, 0, {
28947 x: 0,
28948 y: 0
28949 }, 1);
28950 } else {
28951 shapeImpl.draw(context, size, angle, translation, edgeWidth);
28952 }
28953
28954 if (context.closePath) {
28955 context.closePath();
28956 }
28957 }
28958
28959 context = canvasContext;
28960
28961 if (usePaths) {
28962 // set transform to arrow position/orientation
28963 context.translate(x, y);
28964 context.rotate(angle);
28965 context.scale(size, size);
28966 }
28967
28968 if (fill === 'filled' || fill === 'both') {
28969 if (usePaths) {
28970 context.fill(path);
28971 } else {
28972 context.fill();
28973 }
28974 }
28975
28976 if (fill === 'hollow' || fill === 'both') {
28977 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
28978 context.lineJoin = 'miter';
28979
28980 if (usePaths) {
28981 context.stroke(path);
28982 } else {
28983 context.stroke();
28984 }
28985 }
28986
28987 if (usePaths) {
28988 // reset transform by applying inverse
28989 context.scale(1 / size, 1 / size);
28990 context.rotate(-angle);
28991 context.translate(-x, -y);
28992 }
28993};
28994
28995var CRp$3 = {};
28996
28997CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
28998 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
28999 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
29000 return;
29001 }
29002
29003 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
29004};
29005
29006CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
29007 var r = this;
29008 var pos = node.position();
29009 var nodeX = pos.x;
29010 var nodeY = pos.y;
29011 var styleObj = node.cy().style();
29012 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
29013 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
29014 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
29015 var nodeW = node.width();
29016 var nodeH = node.height();
29017 var paddingX2 = node.padding() * 2;
29018 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29019 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29020 var rs = node._private.rscratch;
29021 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
29022 var shouldClip = clip === 'node';
29023 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
29024 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
29025 var imgW = img.width || img.cachedW;
29026 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
29027
29028 if (null == imgW || null == imgH) {
29029 document.body.appendChild(img); // eslint-disable-line no-undef
29030
29031 imgW = img.cachedW = img.width || img.offsetWidth;
29032 imgH = img.cachedH = img.height || img.offsetHeight;
29033 document.body.removeChild(img); // eslint-disable-line no-undef
29034 }
29035
29036 var w = imgW;
29037 var h = imgH;
29038
29039 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29040 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29041 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29042 } else {
29043 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29044 }
29045 }
29046
29047 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29048 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29049 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29050 } else {
29051 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29052 }
29053 }
29054
29055 if (w === 0 || h === 0) {
29056 return; // no point in drawing empty image (and chrome is broken in this case)
29057 }
29058
29059 if (fit === 'contain') {
29060 var scale = Math.min(nodeTW / w, nodeTH / h);
29061 w *= scale;
29062 h *= scale;
29063 } else if (fit === 'cover') {
29064 var scale = Math.max(nodeTW / w, nodeTH / h);
29065 w *= scale;
29066 h *= scale;
29067 }
29068
29069 var x = nodeX - nodeTW / 2; // left
29070
29071 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29072 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29073
29074 if (posXUnits === '%') {
29075 x += (nodeTW - w) * posXPfVal;
29076 } else {
29077 x += posXPfVal;
29078 }
29079
29080 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29081 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29082
29083 if (offXUnits === '%') {
29084 x += (nodeTW - w) * offXPfVal;
29085 } else {
29086 x += offXPfVal;
29087 }
29088
29089 var y = nodeY - nodeTH / 2; // top
29090
29091 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29092 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29093
29094 if (posYUnits === '%') {
29095 y += (nodeTH - h) * posYPfVal;
29096 } else {
29097 y += posYPfVal;
29098 }
29099
29100 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29101 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29102
29103 if (offYUnits === '%') {
29104 y += (nodeTH - h) * offYPfVal;
29105 } else {
29106 y += offYPfVal;
29107 }
29108
29109 if (rs.pathCache) {
29110 x -= nodeX;
29111 y -= nodeY;
29112 nodeX = 0;
29113 nodeY = 0;
29114 }
29115
29116 var gAlpha = context.globalAlpha;
29117 context.globalAlpha = imgOpacity;
29118 var smoothingEnabled = r.getImgSmoothing(context);
29119 var isSmoothingSwitched = false;
29120
29121 if (smooth === 'no' && smoothingEnabled) {
29122 r.setImgSmoothing(context, false);
29123 isSmoothingSwitched = true;
29124 } else if (smooth === 'yes' && !smoothingEnabled) {
29125 r.setImgSmoothing(context, true);
29126 isSmoothingSwitched = true;
29127 }
29128
29129 if (repeat === 'no-repeat') {
29130 if (shouldClip) {
29131 context.save();
29132
29133 if (rs.pathCache) {
29134 context.clip(rs.pathCache);
29135 } else {
29136 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29137 context.clip();
29138 }
29139 }
29140
29141 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29142
29143 if (shouldClip) {
29144 context.restore();
29145 }
29146 } else {
29147 var pattern = context.createPattern(img, repeat);
29148 context.fillStyle = pattern;
29149 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29150 context.translate(x, y);
29151 context.fill();
29152 context.translate(-x, -y);
29153 }
29154
29155 context.globalAlpha = gAlpha;
29156
29157 if (isSmoothingSwitched) {
29158 r.setImgSmoothing(context, smoothingEnabled);
29159 }
29160};
29161
29162var CRp$4 = {};
29163
29164CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29165 if (!scale) {
29166 var zoom = ele.cy().zoom();
29167 var pxRatio = this.getPixelRatio();
29168 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29169
29170 scale = Math.pow(2, lvl);
29171 }
29172
29173 var computedSize = ele.pstyle('font-size').pfValue * scale;
29174 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29175
29176 if (computedSize < minSize) {
29177 return false;
29178 }
29179
29180 return true;
29181};
29182
29183CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29184 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29185 var r = this;
29186
29187 if (force == null) {
29188 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29189 return;
29190 }
29191 } else if (force === false) {
29192 return;
29193 }
29194
29195 if (ele.isNode()) {
29196 var label = ele.pstyle('label');
29197
29198 if (!label || !label.value) {
29199 return;
29200 }
29201
29202 var justification = r.getLabelJustification(ele);
29203 context.textAlign = justification;
29204 context.textBaseline = 'bottom';
29205 } else {
29206 var badLine = ele.element()._private.rscratch.badLine;
29207
29208 var _label = ele.pstyle('label');
29209
29210 var srcLabel = ele.pstyle('source-label');
29211 var tgtLabel = ele.pstyle('target-label');
29212
29213 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29214 return;
29215 }
29216
29217 context.textAlign = 'center';
29218 context.textBaseline = 'bottom';
29219 }
29220
29221 var applyRotation = !shiftToOriginWithBb;
29222 var bb;
29223
29224 if (shiftToOriginWithBb) {
29225 bb = shiftToOriginWithBb;
29226 context.translate(-bb.x1, -bb.y1);
29227 }
29228
29229 if (prefix == null) {
29230 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29231
29232 if (ele.isEdge()) {
29233 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29234 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29235 }
29236 } else {
29237 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29238 }
29239
29240 if (shiftToOriginWithBb) {
29241 context.translate(bb.x1, bb.y1);
29242 }
29243};
29244
29245CRp$4.getFontCache = function (context) {
29246 var cache;
29247 this.fontCaches = this.fontCaches || [];
29248
29249 for (var i = 0; i < this.fontCaches.length; i++) {
29250 cache = this.fontCaches[i];
29251
29252 if (cache.context === context) {
29253 return cache;
29254 }
29255 }
29256
29257 cache = {
29258 context: context
29259 };
29260 this.fontCaches.push(cache);
29261 return cache;
29262}; // set up canvas context with font
29263// returns transformed text string
29264
29265
29266CRp$4.setupTextStyle = function (context, ele) {
29267 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29268 // Font style
29269 var labelStyle = ele.pstyle('font-style').strValue;
29270 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29271 var labelFamily = ele.pstyle('font-family').strValue;
29272 var labelWeight = ele.pstyle('font-weight').strValue;
29273 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29274 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29275 var color = ele.pstyle('color').value;
29276 var outlineColor = ele.pstyle('text-outline-color').value;
29277 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29278 context.lineJoin = 'round'; // so text outlines aren't jagged
29279
29280 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29281 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29282}; // TODO ensure re-used
29283
29284
29285function roundRect(ctx, x, y, width, height) {
29286 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29287 ctx.beginPath();
29288 ctx.moveTo(x + radius, y);
29289 ctx.lineTo(x + width - radius, y);
29290 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29291 ctx.lineTo(x + width, y + height - radius);
29292 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29293 ctx.lineTo(x + radius, y + height);
29294 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29295 ctx.lineTo(x, y + radius);
29296 ctx.quadraticCurveTo(x, y, x + radius, y);
29297 ctx.closePath();
29298 ctx.fill();
29299}
29300
29301CRp$4.getTextAngle = function (ele, prefix) {
29302 var theta;
29303 var _p = ele._private;
29304 var rscratch = _p.rscratch;
29305 var pdash = prefix ? prefix + '-' : '';
29306 var rotation = ele.pstyle(pdash + 'text-rotation');
29307 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29308
29309 if (rotation.strValue === 'autorotate') {
29310 theta = ele.isEdge() ? textAngle : 0;
29311 } else if (rotation.strValue === 'none') {
29312 theta = 0;
29313 } else {
29314 theta = rotation.pfValue;
29315 }
29316
29317 return theta;
29318};
29319
29320CRp$4.drawText = function (context, ele, prefix) {
29321 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29322 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29323 var _p = ele._private;
29324 var rscratch = _p.rscratch;
29325 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29326
29327 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29328 return;
29329 } // use 'main' as an alias for the main label (i.e. null prefix)
29330
29331
29332 if (prefix === 'main') {
29333 prefix = null;
29334 }
29335
29336 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29337 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29338 var orgTextX, orgTextY; // used for rotation
29339
29340 var text = this.getLabelText(ele, prefix);
29341
29342 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29343 this.setupTextStyle(context, ele, useEleOpacity);
29344 var pdash = prefix ? prefix + '-' : '';
29345 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29346 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29347 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29348 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29349 var isEdge = ele.isEdge();
29350 var halign = ele.pstyle('text-halign').value;
29351 var valign = ele.pstyle('text-valign').value;
29352
29353 if (isEdge) {
29354 halign = 'center';
29355 valign = 'center';
29356 }
29357
29358 textX += marginX;
29359 textY += marginY;
29360 var theta;
29361
29362 if (!applyRotation) {
29363 theta = 0;
29364 } else {
29365 theta = this.getTextAngle(ele, prefix);
29366 }
29367
29368 if (theta !== 0) {
29369 orgTextX = textX;
29370 orgTextY = textY;
29371 context.translate(orgTextX, orgTextY);
29372 context.rotate(theta);
29373 textX = 0;
29374 textY = 0;
29375 }
29376
29377 switch (valign) {
29378 case 'top':
29379 break;
29380
29381 case 'center':
29382 textY += textH / 2;
29383 break;
29384
29385 case 'bottom':
29386 textY += textH;
29387 break;
29388 }
29389
29390 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29391 var borderOpacity = ele.pstyle('text-border-opacity').value;
29392 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29393 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29394
29395 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29396 var bgX = textX - backgroundPadding;
29397
29398 switch (halign) {
29399 case 'left':
29400 bgX -= textW;
29401 break;
29402
29403 case 'center':
29404 bgX -= textW / 2;
29405 break;
29406 }
29407
29408 var bgY = textY - textH - backgroundPadding;
29409 var bgW = textW + 2 * backgroundPadding;
29410 var bgH = textH + 2 * backgroundPadding;
29411
29412 if (backgroundOpacity > 0) {
29413 var textFill = context.fillStyle;
29414 var textBackgroundColor = ele.pstyle('text-background-color').value;
29415 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29416 var styleShape = ele.pstyle('text-background-shape').strValue;
29417
29418 if (styleShape.indexOf('round') === 0) {
29419 roundRect(context, bgX, bgY, bgW, bgH, 2);
29420 } else {
29421 context.fillRect(bgX, bgY, bgW, bgH);
29422 }
29423
29424 context.fillStyle = textFill;
29425 }
29426
29427 if (textBorderWidth > 0 && borderOpacity > 0) {
29428 var textStroke = context.strokeStyle;
29429 var textLineWidth = context.lineWidth;
29430 var textBorderColor = ele.pstyle('text-border-color').value;
29431 var textBorderStyle = ele.pstyle('text-border-style').value;
29432 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29433 context.lineWidth = textBorderWidth;
29434
29435 if (context.setLineDash) {
29436 // for very outofdate browsers
29437 switch (textBorderStyle) {
29438 case 'dotted':
29439 context.setLineDash([1, 1]);
29440 break;
29441
29442 case 'dashed':
29443 context.setLineDash([4, 2]);
29444 break;
29445
29446 case 'double':
29447 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29448
29449 context.setLineDash([]);
29450 break;
29451
29452 case 'solid':
29453 context.setLineDash([]);
29454 break;
29455 }
29456 }
29457
29458 context.strokeRect(bgX, bgY, bgW, bgH);
29459
29460 if (textBorderStyle === 'double') {
29461 var whiteWidth = textBorderWidth / 2;
29462 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29463 }
29464
29465 if (context.setLineDash) {
29466 // for very outofdate browsers
29467 context.setLineDash([]);
29468 }
29469
29470 context.lineWidth = textLineWidth;
29471 context.strokeStyle = textStroke;
29472 }
29473 }
29474
29475 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29476
29477 if (lineWidth > 0) {
29478 context.lineWidth = lineWidth;
29479 }
29480
29481 if (ele.pstyle('text-wrap').value === 'wrap') {
29482 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29483 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29484 var halfTextW = textW / 2;
29485 var justification = this.getLabelJustification(ele);
29486
29487 if (justification === 'auto') ; else if (halign === 'left') {
29488 // auto justification : right
29489 if (justification === 'left') {
29490 textX += -textW;
29491 } else if (justification === 'center') {
29492 textX += -halfTextW;
29493 } // else same as auto
29494
29495 } else if (halign === 'center') {
29496 // auto justfication : center
29497 if (justification === 'left') {
29498 textX += -halfTextW;
29499 } else if (justification === 'right') {
29500 textX += halfTextW;
29501 } // else same as auto
29502
29503 } else if (halign === 'right') {
29504 // auto justification : left
29505 if (justification === 'center') {
29506 textX += halfTextW;
29507 } else if (justification === 'right') {
29508 textX += textW;
29509 } // else same as auto
29510
29511 }
29512
29513 switch (valign) {
29514 case 'top':
29515 textY -= (lines.length - 1) * lineHeight;
29516 break;
29517
29518 case 'center':
29519 case 'bottom':
29520 textY -= (lines.length - 1) * lineHeight;
29521 break;
29522 }
29523
29524 for (var l = 0; l < lines.length; l++) {
29525 if (lineWidth > 0) {
29526 context.strokeText(lines[l], textX, textY);
29527 }
29528
29529 context.fillText(lines[l], textX, textY);
29530 textY += lineHeight;
29531 }
29532 } else {
29533 if (lineWidth > 0) {
29534 context.strokeText(text, textX, textY);
29535 }
29536
29537 context.fillText(text, textX, textY);
29538 }
29539
29540 if (theta !== 0) {
29541 context.rotate(-theta);
29542 context.translate(-orgTextX, -orgTextY);
29543 }
29544 }
29545};
29546
29547/* global Path2D */
29548var CRp$5 = {};
29549
29550CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29551 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29552 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29553 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29554 var r = this;
29555 var nodeWidth, nodeHeight;
29556 var _p = node._private;
29557 var rs = _p.rscratch;
29558 var pos = node.position();
29559
29560 if (!number(pos.x) || !number(pos.y)) {
29561 return; // can't draw node with undefined position
29562 }
29563
29564 if (shouldDrawOpacity && !node.visible()) {
29565 return;
29566 }
29567
29568 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29569 var usePaths = r.usePaths();
29570 var path;
29571 var pathCacheHit = false;
29572 var padding = node.padding();
29573 nodeWidth = node.width() + 2 * padding;
29574 nodeHeight = node.height() + 2 * padding; //
29575 // setup shift
29576
29577 var bb;
29578
29579 if (shiftToOriginWithBb) {
29580 bb = shiftToOriginWithBb;
29581 context.translate(-bb.x1, -bb.y1);
29582 } //
29583 // load bg image
29584
29585
29586 var bgImgProp = node.pstyle('background-image');
29587 var urls = bgImgProp.value;
29588 var urlDefined = new Array(urls.length);
29589 var image = new Array(urls.length);
29590 var numImages = 0;
29591
29592 for (var i = 0; i < urls.length; i++) {
29593 var url = urls[i];
29594 var defd = urlDefined[i] = url != null && url !== 'none';
29595
29596 if (defd) {
29597 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29598 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29599
29600 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29601 _p.backgroundTimestamp = Date.now();
29602 node.emitAndNotify('background');
29603 });
29604 }
29605 } //
29606 // setup styles
29607
29608
29609 var darkness = node.pstyle('background-blacken').value;
29610 var borderWidth = node.pstyle('border-width').pfValue;
29611 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29612 var borderColor = node.pstyle('border-color').value;
29613 var borderStyle = node.pstyle('border-style').value;
29614 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29615 context.lineJoin = 'miter'; // so borders are square with the node shape
29616
29617 var setupShapeColor = function setupShapeColor() {
29618 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29619 r.eleFillStyle(context, node, bgOpy);
29620 };
29621
29622 var setupBorderColor = function setupBorderColor() {
29623 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29624 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29625 }; //
29626 // setup shape
29627
29628
29629 var styleShape = node.pstyle('shape').strValue;
29630 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29631
29632 if (usePaths) {
29633 context.translate(pos.x, pos.y);
29634 var pathCache = r.nodePathCache = r.nodePathCache || [];
29635 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29636 var cachedPath = pathCache[key];
29637
29638 if (cachedPath != null) {
29639 path = cachedPath;
29640 pathCacheHit = true;
29641 rs.pathCache = path;
29642 } else {
29643 path = new Path2D();
29644 pathCache[key] = rs.pathCache = path;
29645 }
29646 }
29647
29648 var drawShape = function drawShape() {
29649 if (!pathCacheHit) {
29650 var npos = pos;
29651
29652 if (usePaths) {
29653 npos = {
29654 x: 0,
29655 y: 0
29656 };
29657 }
29658
29659 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29660 }
29661
29662 if (usePaths) {
29663 context.fill(path);
29664 } else {
29665 context.fill();
29666 }
29667 };
29668
29669 var drawImages = function drawImages() {
29670 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29671 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
29672 var prevBging = _p.backgrounding;
29673 var totalCompleted = 0;
29674
29675 for (var _i = 0; _i < image.length; _i++) {
29676 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
29677
29678 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
29679 totalCompleted++;
29680 continue;
29681 }
29682
29683 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29684 totalCompleted++;
29685 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29686 }
29687 }
29688
29689 _p.backgrounding = !(totalCompleted === numImages);
29690
29691 if (prevBging !== _p.backgrounding) {
29692 // update style b/c :backgrounding state changed
29693 node.updateStyle(false);
29694 }
29695 };
29696
29697 var drawPie = function drawPie() {
29698 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29699 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29700
29701 if (r.hasPie(node)) {
29702 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29703
29704 if (redrawShape) {
29705 if (!usePaths) {
29706 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29707 }
29708 }
29709 }
29710 };
29711
29712 var darken = function darken() {
29713 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29714 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29715 var c = darkness > 0 ? 0 : 255;
29716
29717 if (darkness !== 0) {
29718 r.colorFillStyle(context, c, c, c, opacity);
29719
29720 if (usePaths) {
29721 context.fill(path);
29722 } else {
29723 context.fill();
29724 }
29725 }
29726 };
29727
29728 var drawBorder = function drawBorder() {
29729 if (borderWidth > 0) {
29730 context.lineWidth = borderWidth;
29731 context.lineCap = 'butt';
29732
29733 if (context.setLineDash) {
29734 // for very outofdate browsers
29735 switch (borderStyle) {
29736 case 'dotted':
29737 context.setLineDash([1, 1]);
29738 break;
29739
29740 case 'dashed':
29741 context.setLineDash([4, 2]);
29742 break;
29743
29744 case 'solid':
29745 case 'double':
29746 context.setLineDash([]);
29747 break;
29748 }
29749 }
29750
29751 if (usePaths) {
29752 context.stroke(path);
29753 } else {
29754 context.stroke();
29755 }
29756
29757 if (borderStyle === 'double') {
29758 context.lineWidth = borderWidth / 3;
29759 var gco = context.globalCompositeOperation;
29760 context.globalCompositeOperation = 'destination-out';
29761
29762 if (usePaths) {
29763 context.stroke(path);
29764 } else {
29765 context.stroke();
29766 }
29767
29768 context.globalCompositeOperation = gco;
29769 } // reset in case we changed the border style
29770
29771
29772 if (context.setLineDash) {
29773 // for very outofdate browsers
29774 context.setLineDash([]);
29775 }
29776 }
29777 };
29778
29779 var drawOverlay = function drawOverlay() {
29780 if (shouldDrawOverlay) {
29781 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
29782 }
29783 };
29784
29785 var drawText = function drawText() {
29786 r.drawElementText(context, node, null, drawLabel);
29787 };
29788
29789 var ghost = node.pstyle('ghost').value === 'yes';
29790
29791 if (ghost) {
29792 var gx = node.pstyle('ghost-offset-x').pfValue;
29793 var gy = node.pstyle('ghost-offset-y').pfValue;
29794 var ghostOpacity = node.pstyle('ghost-opacity').value;
29795 var effGhostOpacity = ghostOpacity * eleOpacity;
29796 context.translate(gx, gy);
29797 setupShapeColor(ghostOpacity * bgOpacity);
29798 drawShape();
29799 drawImages(effGhostOpacity, true);
29800 setupBorderColor(ghostOpacity * borderOpacity);
29801 drawBorder();
29802 drawPie(darkness !== 0 || borderWidth !== 0);
29803 drawImages(effGhostOpacity, false);
29804 darken(effGhostOpacity);
29805 context.translate(-gx, -gy);
29806 }
29807
29808 setupShapeColor();
29809 drawShape();
29810 drawImages(eleOpacity, true);
29811 setupBorderColor();
29812 drawBorder();
29813 drawPie(darkness !== 0 || borderWidth !== 0);
29814 drawImages(eleOpacity, false);
29815 darken();
29816
29817 if (usePaths) {
29818 context.translate(-pos.x, -pos.y);
29819 }
29820
29821 drawText();
29822 drawOverlay(); //
29823 // clean up shift
29824
29825 if (shiftToOriginWithBb) {
29826 context.translate(bb.x1, bb.y1);
29827 }
29828};
29829
29830CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
29831 var r = this;
29832
29833 if (!node.visible()) {
29834 return;
29835 }
29836
29837 var overlayPadding = node.pstyle('overlay-padding').pfValue;
29838 var overlayOpacity = node.pstyle('overlay-opacity').value;
29839 var overlayColor = node.pstyle('overlay-color').value;
29840
29841 if (overlayOpacity > 0) {
29842 pos = pos || node.position();
29843
29844 if (nodeWidth == null || nodeHeight == null) {
29845 var padding = node.padding();
29846 nodeWidth = node.width() + 2 * padding;
29847 nodeHeight = node.height() + 2 * padding;
29848 }
29849
29850 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29851 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
29852 context.fill();
29853 }
29854}; // does the node have at least one pie piece?
29855
29856
29857CRp$5.hasPie = function (node) {
29858 node = node[0]; // ensure ele ref
29859
29860 return node._private.hasPie;
29861};
29862
29863CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
29864 node = node[0]; // ensure ele ref
29865
29866 pos = pos || node.position();
29867 var cyStyle = node.cy().style();
29868 var pieSize = node.pstyle('pie-size');
29869 var x = pos.x;
29870 var y = pos.y;
29871 var nodeW = node.width();
29872 var nodeH = node.height();
29873 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
29874
29875 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
29876
29877 var usePaths = this.usePaths();
29878
29879 if (usePaths) {
29880 x = 0;
29881 y = 0;
29882 }
29883
29884 if (pieSize.units === '%') {
29885 radius = radius * pieSize.pfValue;
29886 } else if (pieSize.pfValue !== undefined) {
29887 radius = pieSize.pfValue / 2;
29888 }
29889
29890 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
29891 // 1..N
29892 var size = node.pstyle('pie-' + i + '-background-size').value;
29893 var color = node.pstyle('pie-' + i + '-background-color').value;
29894 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
29895 var percent = size / 100; // map integer range [0, 100] to [0, 1]
29896 // percent can't push beyond 1
29897
29898 if (percent + lastPercent > 1) {
29899 percent = 1 - lastPercent;
29900 }
29901
29902 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
29903
29904 var angleDelta = 2 * Math.PI * percent;
29905 var angleEnd = angleStart + angleDelta; // ignore if
29906 // - zero size
29907 // - we're already beyond the full circle
29908 // - adding the current slice would go beyond the full circle
29909
29910 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
29911 continue;
29912 }
29913
29914 context.beginPath();
29915 context.moveTo(x, y);
29916 context.arc(x, y, radius, angleStart, angleEnd);
29917 context.closePath();
29918 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29919 context.fill();
29920 lastPercent += percent;
29921 }
29922};
29923
29924var CRp$6 = {};
29925var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
29926
29927CRp$6.getPixelRatio = function () {
29928 var context = this.data.contexts[0];
29929
29930 if (this.forcedPixelRatio != null) {
29931 return this.forcedPixelRatio;
29932 }
29933
29934 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
29935 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
29936};
29937
29938CRp$6.paintCache = function (context) {
29939 var caches = this.paintCaches = this.paintCaches || [];
29940 var needToCreateCache = true;
29941 var cache;
29942
29943 for (var i = 0; i < caches.length; i++) {
29944 cache = caches[i];
29945
29946 if (cache.context === context) {
29947 needToCreateCache = false;
29948 break;
29949 }
29950 }
29951
29952 if (needToCreateCache) {
29953 cache = {
29954 context: context
29955 };
29956 caches.push(cache);
29957 }
29958
29959 return cache;
29960};
29961
29962CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
29963 var gradientStyle;
29964 var usePaths = this.usePaths();
29965 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
29966 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
29967
29968 if (fill === 'radial-gradient') {
29969 if (ele.isEdge()) {
29970 var start = ele.sourceEndpoint(),
29971 end = ele.targetEndpoint(),
29972 mid = ele.midpoint();
29973 var d1 = dist(start, mid);
29974 var d2 = dist(end, mid);
29975 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
29976 } else {
29977 var pos = usePaths ? {
29978 x: 0,
29979 y: 0
29980 } : ele.position(),
29981 width = ele.paddedWidth(),
29982 height = ele.paddedHeight();
29983 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
29984 }
29985 } else {
29986 if (ele.isEdge()) {
29987 var _start = ele.sourceEndpoint(),
29988 _end = ele.targetEndpoint();
29989
29990 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
29991 } else {
29992 var _pos = usePaths ? {
29993 x: 0,
29994 y: 0
29995 } : ele.position(),
29996 _width = ele.paddedWidth(),
29997 _height = ele.paddedHeight(),
29998 halfWidth = _width / 2,
29999 halfHeight = _height / 2;
30000
30001 var direction = ele.pstyle('background-gradient-direction').value;
30002
30003 switch (direction) {
30004 case 'to-bottom':
30005 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
30006 break;
30007
30008 case 'to-top':
30009 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
30010 break;
30011
30012 case 'to-left':
30013 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
30014 break;
30015
30016 case 'to-right':
30017 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
30018 break;
30019
30020 case 'to-bottom-right':
30021 case 'to-right-bottom':
30022 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
30023 break;
30024
30025 case 'to-top-right':
30026 case 'to-right-top':
30027 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
30028 break;
30029
30030 case 'to-bottom-left':
30031 case 'to-left-bottom':
30032 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
30033 break;
30034
30035 case 'to-top-left':
30036 case 'to-left-top':
30037 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
30038 break;
30039 }
30040 }
30041 }
30042
30043 if (!gradientStyle) return null; // invalid gradient style
30044
30045 var hasPositions = positions.length === colors.length;
30046 var length = colors.length;
30047
30048 for (var i = 0; i < length; i++) {
30049 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
30050 }
30051
30052 return gradientStyle;
30053};
30054
30055CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
30056 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30057 if (!gradientStyle) return null; // error
30058
30059 context.fillStyle = gradientStyle;
30060};
30061
30062CRp$6.colorFillStyle = function (context, r, g, b, a) {
30063 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30064 // var cache = this.paintCache(context);
30065 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30066 // if( cache.fillStyle !== fillStyle ){
30067 // context.fillStyle = cache.fillStyle = fillStyle;
30068 // }
30069};
30070
30071CRp$6.eleFillStyle = function (context, ele, opacity) {
30072 var backgroundFill = ele.pstyle('background-fill').value;
30073
30074 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30075 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30076 } else {
30077 var backgroundColor = ele.pstyle('background-color').value;
30078 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30079 }
30080};
30081
30082CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
30083 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30084 if (!gradientStyle) return null; // error
30085
30086 context.strokeStyle = gradientStyle;
30087};
30088
30089CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
30090 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30091 // var cache = this.paintCache(context);
30092 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30093 // if( cache.strokeStyle !== strokeStyle ){
30094 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30095 // }
30096};
30097
30098CRp$6.eleStrokeStyle = function (context, ele, opacity) {
30099 var lineFill = ele.pstyle('line-fill').value;
30100
30101 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30102 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30103 } else {
30104 var lineColor = ele.pstyle('line-color').value;
30105 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30106 }
30107}; // Resize canvas
30108
30109
30110CRp$6.matchCanvasSize = function (container) {
30111 var r = this;
30112 var data = r.data;
30113 var bb = r.findContainerClientCoords();
30114 var width = bb[2];
30115 var height = bb[3];
30116 var pixelRatio = r.getPixelRatio();
30117 var mbPxRatio = r.motionBlurPxRatio;
30118
30119 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30120 pixelRatio = mbPxRatio;
30121 }
30122
30123 var canvasWidth = width * pixelRatio;
30124 var canvasHeight = height * pixelRatio;
30125 var canvas;
30126
30127 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30128 return; // save cycles if same
30129 }
30130
30131 r.fontCaches = null; // resizing resets the style
30132
30133 var canvasContainer = data.canvasContainer;
30134 canvasContainer.style.width = width + 'px';
30135 canvasContainer.style.height = height + 'px';
30136
30137 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30138 canvas = data.canvases[i];
30139 canvas.width = canvasWidth;
30140 canvas.height = canvasHeight;
30141 canvas.style.width = width + 'px';
30142 canvas.style.height = height + 'px';
30143 }
30144
30145 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30146 canvas = data.bufferCanvases[i];
30147 canvas.width = canvasWidth;
30148 canvas.height = canvasHeight;
30149 canvas.style.width = width + 'px';
30150 canvas.style.height = height + 'px';
30151 }
30152
30153 r.textureMult = 1;
30154
30155 if (pixelRatio <= 1) {
30156 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30157 r.textureMult = 2;
30158 canvas.width = canvasWidth * r.textureMult;
30159 canvas.height = canvasHeight * r.textureMult;
30160 }
30161
30162 r.canvasWidth = canvasWidth;
30163 r.canvasHeight = canvasHeight;
30164};
30165
30166CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30167 this.render({
30168 forcedContext: cxt,
30169 forcedZoom: zoom,
30170 forcedPan: pan,
30171 drawAllLayers: true,
30172 forcedPxRatio: pxRatio
30173 });
30174};
30175
30176CRp$6.render = function (options) {
30177 options = options || staticEmptyObject();
30178 var forcedContext = options.forcedContext;
30179 var drawAllLayers = options.drawAllLayers;
30180 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30181 var forcedZoom = options.forcedZoom;
30182 var forcedPan = options.forcedPan;
30183 var r = this;
30184 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30185 var cy = r.cy;
30186 var data = r.data;
30187 var needDraw = data.canvasNeedsRedraw;
30188 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30189 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30190 var mbPxRatio = r.motionBlurPxRatio;
30191 var hasCompoundNodes = cy.hasCompoundNodes();
30192 var inNodeDragGesture = r.hoverData.draggingEles;
30193 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30194 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30195 var motionBlurFadeEffect = motionBlur;
30196
30197 if (!forcedContext) {
30198 if (r.prevPxRatio !== pixelRatio) {
30199 r.invalidateContainerClientCoordsCache();
30200 r.matchCanvasSize(r.container);
30201 r.redrawHint('eles', true);
30202 r.redrawHint('drag', true);
30203 }
30204
30205 r.prevPxRatio = pixelRatio;
30206 }
30207
30208 if (!forcedContext && r.motionBlurTimeout) {
30209 clearTimeout(r.motionBlurTimeout);
30210 }
30211
30212 if (motionBlur) {
30213 if (r.mbFrames == null) {
30214 r.mbFrames = 0;
30215 }
30216
30217 r.mbFrames++;
30218
30219 if (r.mbFrames < 3) {
30220 // need several frames before even high quality motionblur
30221 motionBlurFadeEffect = false;
30222 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30223
30224
30225 if (r.mbFrames > r.minMbLowQualFrames) {
30226 //r.fullQualityMb = false;
30227 r.motionBlurPxRatio = r.mbPxRBlurry;
30228 }
30229 }
30230
30231 if (r.clearingMotionBlur) {
30232 r.motionBlurPxRatio = 1;
30233 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30234 // because a rogue async texture frame would clear needDraw
30235
30236
30237 if (r.textureDrawLastFrame && !textureDraw) {
30238 needDraw[r.NODE] = true;
30239 needDraw[r.SELECT_BOX] = true;
30240 }
30241
30242 var style = cy.style();
30243 var zoom = cy.zoom();
30244 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30245 var pan = cy.pan();
30246 var effectivePan = {
30247 x: pan.x,
30248 y: pan.y
30249 };
30250 var vp = {
30251 zoom: zoom,
30252 pan: {
30253 x: pan.x,
30254 y: pan.y
30255 }
30256 };
30257 var prevVp = r.prevViewport;
30258 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)
30259
30260 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30261 r.motionBlurPxRatio = 1;
30262 }
30263
30264 if (forcedPan) {
30265 effectivePan = forcedPan;
30266 } // apply pixel ratio
30267
30268
30269 effectiveZoom *= pixelRatio;
30270 effectivePan.x *= pixelRatio;
30271 effectivePan.y *= pixelRatio;
30272 var eles = r.getCachedZSortedEles();
30273
30274 function mbclear(context, x, y, w, h) {
30275 var gco = context.globalCompositeOperation;
30276 context.globalCompositeOperation = 'destination-out';
30277 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30278 context.fillRect(x, y, w, h);
30279 context.globalCompositeOperation = gco;
30280 }
30281
30282 function setContextTransform(context, clear) {
30283 var ePan, eZoom, w, h;
30284
30285 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30286 ePan = {
30287 x: pan.x * mbPxRatio,
30288 y: pan.y * mbPxRatio
30289 };
30290 eZoom = zoom * mbPxRatio;
30291 w = r.canvasWidth * mbPxRatio;
30292 h = r.canvasHeight * mbPxRatio;
30293 } else {
30294 ePan = effectivePan;
30295 eZoom = effectiveZoom;
30296 w = r.canvasWidth;
30297 h = r.canvasHeight;
30298 }
30299
30300 context.setTransform(1, 0, 0, 1, 0, 0);
30301
30302 if (clear === 'motionBlur') {
30303 mbclear(context, 0, 0, w, h);
30304 } else if (!forcedContext && (clear === undefined || clear)) {
30305 context.clearRect(0, 0, w, h);
30306 }
30307
30308 if (!drawAllLayers) {
30309 context.translate(ePan.x, ePan.y);
30310 context.scale(eZoom, eZoom);
30311 }
30312
30313 if (forcedPan) {
30314 context.translate(forcedPan.x, forcedPan.y);
30315 }
30316
30317 if (forcedZoom) {
30318 context.scale(forcedZoom, forcedZoom);
30319 }
30320 }
30321
30322 if (!textureDraw) {
30323 r.textureDrawLastFrame = false;
30324 }
30325
30326 if (textureDraw) {
30327 r.textureDrawLastFrame = true;
30328
30329 if (!r.textureCache) {
30330 r.textureCache = {};
30331 r.textureCache.bb = cy.mutableElements().boundingBox();
30332 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30333 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30334 cxt.setTransform(1, 0, 0, 1, 0, 0);
30335 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30336 r.render({
30337 forcedContext: cxt,
30338 drawOnlyNodeLayer: true,
30339 forcedPxRatio: pixelRatio * r.textureMult
30340 });
30341 var vp = r.textureCache.viewport = {
30342 zoom: cy.zoom(),
30343 pan: cy.pan(),
30344 width: r.canvasWidth,
30345 height: r.canvasHeight
30346 };
30347 vp.mpan = {
30348 x: (0 - vp.pan.x) / vp.zoom,
30349 y: (0 - vp.pan.y) / vp.zoom
30350 };
30351 }
30352
30353 needDraw[r.DRAG] = false;
30354 needDraw[r.NODE] = false;
30355 var context = data.contexts[r.NODE];
30356 var texture = r.textureCache.texture;
30357 var vp = r.textureCache.viewport;
30358 context.setTransform(1, 0, 0, 1, 0, 0);
30359
30360 if (motionBlur) {
30361 mbclear(context, 0, 0, vp.width, vp.height);
30362 } else {
30363 context.clearRect(0, 0, vp.width, vp.height);
30364 }
30365
30366 var outsideBgColor = style.core('outside-texture-bg-color').value;
30367 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30368 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30369 context.fillRect(0, 0, vp.width, vp.height);
30370 var zoom = cy.zoom();
30371 setContextTransform(context, false);
30372 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30373 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30374 } else if (r.textureOnViewport && !forcedContext) {
30375 // clear the cache since we don't need it
30376 r.textureCache = null;
30377 }
30378
30379 var extent = cy.extent();
30380 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30381 var hideEdges = r.hideEdgesOnViewport && vpManip;
30382 var needMbClear = [];
30383 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30384
30385 if (needMbClear[r.NODE]) {
30386 r.clearedForMotionBlur[r.NODE] = true;
30387 }
30388
30389 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30390
30391 if (needMbClear[r.DRAG]) {
30392 r.clearedForMotionBlur[r.DRAG] = true;
30393 }
30394
30395 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30396 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30397 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30398 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30399 setContextTransform(context, clear);
30400
30401 if (hideEdges) {
30402 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30403 } else {
30404 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30405 }
30406
30407 if (r.debug) {
30408 r.drawDebugPoints(context, eles.nondrag);
30409 }
30410
30411 if (!drawAllLayers && !motionBlur) {
30412 needDraw[r.NODE] = false;
30413 }
30414 }
30415
30416 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30417 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30418 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30419 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30420
30421 if (hideEdges) {
30422 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30423 } else {
30424 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30425 }
30426
30427 if (r.debug) {
30428 r.drawDebugPoints(context, eles.drag);
30429 }
30430
30431 if (!drawAllLayers && !motionBlur) {
30432 needDraw[r.DRAG] = false;
30433 }
30434 }
30435
30436 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30437 var context = forcedContext || data.contexts[r.SELECT_BOX];
30438 setContextTransform(context);
30439
30440 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30441 var zoom = r.cy.zoom();
30442 var borderWidth = style.core('selection-box-border-width').value / zoom;
30443 context.lineWidth = borderWidth;
30444 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 + ')';
30445 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30446
30447 if (borderWidth > 0) {
30448 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 + ')';
30449 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30450 }
30451 }
30452
30453 if (data.bgActivePosistion && !r.hoverData.selecting) {
30454 var zoom = r.cy.zoom();
30455 var pos = data.bgActivePosistion;
30456 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 + ')';
30457 context.beginPath();
30458 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30459 context.fill();
30460 }
30461
30462 var timeToRender = r.lastRedrawTime;
30463
30464 if (r.showFps && timeToRender) {
30465 timeToRender = Math.round(timeToRender);
30466 var fps = Math.round(1000 / timeToRender);
30467 context.setTransform(1, 0, 0, 1, 0, 0);
30468 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30469 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30470 context.lineWidth = 1;
30471 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30472 var maxFps = 60;
30473 context.strokeRect(0, 30, 250, 20);
30474 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30475 }
30476
30477 if (!drawAllLayers) {
30478 needDraw[r.SELECT_BOX] = false;
30479 }
30480 } // motionblur: blit rendered blurry frames
30481
30482
30483 if (motionBlur && mbPxRatio !== 1) {
30484 var cxtNode = data.contexts[r.NODE];
30485 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30486 var cxtDrag = data.contexts[r.DRAG];
30487 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30488
30489 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30490 cxt.setTransform(1, 0, 0, 1, 0, 0);
30491
30492 if (needClear || !motionBlurFadeEffect) {
30493 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30494 } else {
30495 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30496 }
30497
30498 var pxr = mbPxRatio;
30499 cxt.drawImage(txt, // img
30500 0, 0, // sx, sy
30501 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30502 0, 0, // x, y
30503 r.canvasWidth, r.canvasHeight // w, h
30504 );
30505 };
30506
30507 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30508 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30509 needDraw[r.NODE] = false;
30510 }
30511
30512 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30513 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30514 needDraw[r.DRAG] = false;
30515 }
30516 }
30517
30518 r.prevViewport = vp;
30519
30520 if (r.clearingMotionBlur) {
30521 r.clearingMotionBlur = false;
30522 r.motionBlurCleared = true;
30523 r.motionBlur = true;
30524 }
30525
30526 if (motionBlur) {
30527 r.motionBlurTimeout = setTimeout(function () {
30528 r.motionBlurTimeout = null;
30529 r.clearedForMotionBlur[r.NODE] = false;
30530 r.clearedForMotionBlur[r.DRAG] = false;
30531 r.motionBlur = false;
30532 r.clearingMotionBlur = !textureDraw;
30533 r.mbFrames = 0;
30534 needDraw[r.NODE] = true;
30535 needDraw[r.DRAG] = true;
30536 r.redraw();
30537 }, motionBlurDelay);
30538 }
30539
30540 if (!forcedContext) {
30541 cy.emit('render');
30542 }
30543};
30544
30545var CRp$7 = {}; // @O Polygon drawing
30546
30547CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
30548 var halfW = width / 2;
30549 var halfH = height / 2;
30550
30551 if (context.beginPath) {
30552 context.beginPath();
30553 }
30554
30555 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30556
30557 for (var i = 1; i < points.length / 2; i++) {
30558 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30559 }
30560
30561 context.closePath();
30562};
30563
30564CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30565 var halfW = width / 2;
30566 var halfH = height / 2;
30567 var cornerRadius = getRoundPolygonRadius(width, height);
30568
30569 if (context.beginPath) {
30570 context.beginPath();
30571 }
30572
30573 for (var _i = 0; _i < points.length / 4; _i++) {
30574 var sourceUv = void 0,
30575 destUv = void 0;
30576
30577 if (_i === 0) {
30578 sourceUv = points.length - 2;
30579 } else {
30580 sourceUv = _i * 4 - 2;
30581 }
30582
30583 destUv = _i * 4 + 2;
30584 var px = x + halfW * points[_i * 4];
30585 var py = y + halfH * points[_i * 4 + 1];
30586 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30587 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30588 var cp0x = px - offset * points[sourceUv];
30589 var cp0y = py - offset * points[sourceUv + 1];
30590 var cp1x = px + offset * points[destUv];
30591 var cp1y = py + offset * points[destUv + 1];
30592
30593 if (_i === 0) {
30594 context.moveTo(cp0x, cp0y);
30595 } else {
30596 context.lineTo(cp0x, cp0y);
30597 }
30598
30599 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30600 }
30601
30602 context.closePath();
30603}; // Round rectangle drawing
30604
30605
30606CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
30607 var halfWidth = width / 2;
30608 var halfHeight = height / 2;
30609 var cornerRadius = getRoundRectangleRadius(width, height);
30610
30611 if (context.beginPath) {
30612 context.beginPath();
30613 } // Start at top middle
30614
30615
30616 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30617
30618 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30619
30620 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30621
30622 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30623
30624 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30625
30626 context.lineTo(x, y - halfHeight);
30627 context.closePath();
30628};
30629
30630CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30631 var halfWidth = width / 2;
30632 var halfHeight = height / 2;
30633 var cornerRadius = getRoundRectangleRadius(width, height);
30634
30635 if (context.beginPath) {
30636 context.beginPath();
30637 } // Start at top middle
30638
30639
30640 context.moveTo(x, y - halfHeight);
30641 context.lineTo(x + halfWidth, y - halfHeight);
30642 context.lineTo(x + halfWidth, y);
30643 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30644 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30645 context.lineTo(x - halfWidth, y - halfHeight);
30646 context.lineTo(x, y - halfHeight);
30647 context.closePath();
30648};
30649
30650CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
30651 var halfWidth = width / 2;
30652 var halfHeight = height / 2;
30653 var cornerLength = getCutRectangleCornerLength();
30654
30655 if (context.beginPath) {
30656 context.beginPath();
30657 }
30658
30659 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30660 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30661 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30662 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30663 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30664 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30665 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30666 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30667 context.closePath();
30668};
30669
30670CRp$7.drawBarrelPath = function (context, x, y, width, height) {
30671 var halfWidth = width / 2;
30672 var halfHeight = height / 2;
30673 var xBegin = x - halfWidth;
30674 var xEnd = x + halfWidth;
30675 var yBegin = y - halfHeight;
30676 var yEnd = y + halfHeight;
30677 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30678 var wOffset = barrelCurveConstants.widthOffset;
30679 var hOffset = barrelCurveConstants.heightOffset;
30680 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30681
30682 if (context.beginPath) {
30683 context.beginPath();
30684 }
30685
30686 context.moveTo(xBegin, yBegin + hOffset);
30687 context.lineTo(xBegin, yEnd - hOffset);
30688 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30689 context.lineTo(xEnd - wOffset, yEnd);
30690 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30691 context.lineTo(xEnd, yBegin + hOffset);
30692 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30693 context.lineTo(xBegin + wOffset, yBegin);
30694 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30695 context.closePath();
30696};
30697
30698var sin0 = Math.sin(0);
30699var cos0 = Math.cos(0);
30700var sin = {};
30701var cos = {};
30702var ellipseStepSize = Math.PI / 40;
30703
30704for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30705 sin[i] = Math.sin(i);
30706 cos[i] = Math.cos(i);
30707}
30708
30709CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
30710 if (context.beginPath) {
30711 context.beginPath();
30712 }
30713
30714 if (context.ellipse) {
30715 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30716 } else {
30717 var xPos, yPos;
30718 var rw = width / 2;
30719 var rh = height / 2;
30720
30721 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30722 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30723 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30724
30725 if (i === 0) {
30726 context.moveTo(xPos, yPos);
30727 } else {
30728 context.lineTo(xPos, yPos);
30729 }
30730 }
30731 }
30732
30733 context.closePath();
30734};
30735
30736/* global atob, ArrayBuffer, Uint8Array, Blob */
30737var CRp$8 = {};
30738
30739CRp$8.createBuffer = function (w, h) {
30740 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30741
30742 buffer.width = w;
30743 buffer.height = h;
30744 return [buffer, buffer.getContext('2d')];
30745};
30746
30747CRp$8.bufferCanvasImage = function (options) {
30748 var cy = this.cy;
30749 var eles = cy.mutableElements();
30750 var bb = eles.boundingBox();
30751 var ctrRect = this.findContainerClientCoords();
30752 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
30753 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
30754 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
30755 var pxRatio = this.getPixelRatio();
30756 var scale = 1;
30757
30758 if (options.scale !== undefined) {
30759 width *= options.scale;
30760 height *= options.scale;
30761 scale = options.scale;
30762 } else if (specdMaxDims) {
30763 var maxScaleW = Infinity;
30764 var maxScaleH = Infinity;
30765
30766 if (number(options.maxWidth)) {
30767 maxScaleW = scale * options.maxWidth / width;
30768 }
30769
30770 if (number(options.maxHeight)) {
30771 maxScaleH = scale * options.maxHeight / height;
30772 }
30773
30774 scale = Math.min(maxScaleW, maxScaleH);
30775 width *= scale;
30776 height *= scale;
30777 }
30778
30779 if (!specdMaxDims) {
30780 width *= pxRatio;
30781 height *= pxRatio;
30782 scale *= pxRatio;
30783 }
30784
30785 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
30786
30787 buffCanvas.width = width;
30788 buffCanvas.height = height;
30789 buffCanvas.style.width = width + 'px';
30790 buffCanvas.style.height = height + 'px';
30791 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
30792
30793 if (width > 0 && height > 0) {
30794 buffCxt.clearRect(0, 0, width, height);
30795 buffCxt.globalCompositeOperation = 'source-over';
30796 var zsortedEles = this.getCachedZSortedEles();
30797
30798 if (options.full) {
30799 // draw the full bounds of the graph
30800 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
30801 buffCxt.scale(scale, scale);
30802 this.drawElements(buffCxt, zsortedEles);
30803 buffCxt.scale(1 / scale, 1 / scale);
30804 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
30805 } else {
30806 // draw the current view
30807 var pan = cy.pan();
30808 var translation = {
30809 x: pan.x * scale,
30810 y: pan.y * scale
30811 };
30812 scale *= cy.zoom();
30813 buffCxt.translate(translation.x, translation.y);
30814 buffCxt.scale(scale, scale);
30815 this.drawElements(buffCxt, zsortedEles);
30816 buffCxt.scale(1 / scale, 1 / scale);
30817 buffCxt.translate(-translation.x, -translation.y);
30818 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
30819
30820
30821 if (options.bg) {
30822 buffCxt.globalCompositeOperation = 'destination-over';
30823 buffCxt.fillStyle = options.bg;
30824 buffCxt.rect(0, 0, width, height);
30825 buffCxt.fill();
30826 }
30827 }
30828
30829 return buffCanvas;
30830};
30831
30832function b64ToBlob(b64, mimeType) {
30833 var bytes = atob(b64);
30834 var buff = new ArrayBuffer(bytes.length);
30835 var buffUint8 = new Uint8Array(buff);
30836
30837 for (var i = 0; i < bytes.length; i++) {
30838 buffUint8[i] = bytes.charCodeAt(i);
30839 }
30840
30841 return new Blob([buff], {
30842 type: mimeType
30843 });
30844}
30845
30846function b64UriToB64(b64uri) {
30847 var i = b64uri.indexOf(',');
30848 return b64uri.substr(i + 1);
30849}
30850
30851function output(options, canvas, mimeType) {
30852 var getB64Uri = function getB64Uri() {
30853 return canvas.toDataURL(mimeType, options.quality);
30854 };
30855
30856 switch (options.output) {
30857 case 'blob-promise':
30858 return new Promise$1(function (resolve, reject) {
30859 try {
30860 canvas.toBlob(function (blob) {
30861 if (blob != null) {
30862 resolve(blob);
30863 } else {
30864 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
30865 }
30866 }, mimeType, options.quality);
30867 } catch (err) {
30868 reject(err);
30869 }
30870 });
30871
30872 case 'blob':
30873 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
30874
30875 case 'base64':
30876 return b64UriToB64(getB64Uri());
30877
30878 case 'base64uri':
30879 default:
30880 return getB64Uri();
30881 }
30882}
30883
30884CRp$8.png = function (options) {
30885 return output(options, this.bufferCanvasImage(options), 'image/png');
30886};
30887
30888CRp$8.jpg = function (options) {
30889 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
30890};
30891
30892var CRp$9 = {};
30893
30894CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
30895 switch (name) {
30896 case 'ellipse':
30897 return this.drawEllipsePath(context, centerX, centerY, width, height);
30898
30899 case 'polygon':
30900 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
30901
30902 case 'round-polygon':
30903 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
30904
30905 case 'roundrectangle':
30906 case 'round-rectangle':
30907 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
30908
30909 case 'cutrectangle':
30910 case 'cut-rectangle':
30911 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
30912
30913 case 'bottomroundrectangle':
30914 case 'bottom-round-rectangle':
30915 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
30916
30917 case 'barrel':
30918 return this.drawBarrelPath(context, centerX, centerY, width, height);
30919 }
30920};
30921
30922var CR = CanvasRenderer;
30923var CRp$a = CanvasRenderer.prototype;
30924CRp$a.CANVAS_LAYERS = 3; //
30925
30926CRp$a.SELECT_BOX = 0;
30927CRp$a.DRAG = 1;
30928CRp$a.NODE = 2;
30929CRp$a.BUFFER_COUNT = 3; //
30930
30931CRp$a.TEXTURE_BUFFER = 0;
30932CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
30933CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
30934
30935function CanvasRenderer(options) {
30936 var r = this;
30937 r.data = {
30938 canvases: new Array(CRp$a.CANVAS_LAYERS),
30939 contexts: new Array(CRp$a.CANVAS_LAYERS),
30940 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
30941 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
30942 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
30943 };
30944 var tapHlOffAttr = '-webkit-tap-highlight-color';
30945 var tapHlOffStyle = 'rgba(0,0,0,0)';
30946 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
30947
30948 var containerStyle = r.data.canvasContainer.style;
30949 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
30950 containerStyle.position = 'relative';
30951 containerStyle.zIndex = '0';
30952 containerStyle.overflow = 'hidden';
30953 var container = options.cy.container();
30954 container.appendChild(r.data.canvasContainer);
30955 container.style[tapHlOffAttr] = tapHlOffStyle;
30956 var styleMap = {
30957 '-webkit-user-select': 'none',
30958 '-moz-user-select': '-moz-none',
30959 'user-select': 'none',
30960 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
30961 'outline-style': 'none'
30962 };
30963
30964 if (ms()) {
30965 styleMap['-ms-touch-action'] = 'none';
30966 styleMap['touch-action'] = 'none';
30967 }
30968
30969 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
30970 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30971
30972 r.data.contexts[i] = canvas.getContext('2d');
30973 Object.keys(styleMap).forEach(function (k) {
30974 canvas.style[k] = styleMap[k];
30975 });
30976 canvas.style.position = 'absolute';
30977 canvas.setAttribute('data-id', 'layer' + i);
30978 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
30979 r.data.canvasContainer.appendChild(canvas);
30980 r.data.canvasNeedsRedraw[i] = false;
30981 }
30982
30983 r.data.topCanvas = r.data.canvases[0];
30984 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
30985 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
30986 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
30987
30988 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
30989 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30990
30991 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
30992 r.data.bufferCanvases[i].style.position = 'absolute';
30993 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
30994 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
30995 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
30996 }
30997
30998 r.pathsEnabled = true;
30999 var emptyBb = makeBoundingBox();
31000
31001 var getBoxCenter = function getBoxCenter(bb) {
31002 return {
31003 x: (bb.x1 + bb.x2) / 2,
31004 y: (bb.y1 + bb.y2) / 2
31005 };
31006 };
31007
31008 var getCenterOffset = function getCenterOffset(bb) {
31009 return {
31010 x: -bb.w / 2,
31011 y: -bb.h / 2
31012 };
31013 };
31014
31015 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
31016 var _p = ele[0]._private;
31017 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
31018 return !same;
31019 };
31020
31021 var getStyleKey = function getStyleKey(ele) {
31022 return ele[0]._private.nodeKey;
31023 };
31024
31025 var getLabelKey = function getLabelKey(ele) {
31026 return ele[0]._private.labelStyleKey;
31027 };
31028
31029 var getSourceLabelKey = function getSourceLabelKey(ele) {
31030 return ele[0]._private.sourceLabelStyleKey;
31031 };
31032
31033 var getTargetLabelKey = function getTargetLabelKey(ele) {
31034 return ele[0]._private.targetLabelStyleKey;
31035 };
31036
31037 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
31038 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
31039 };
31040
31041 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31042 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
31043 };
31044
31045 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31046 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
31047 };
31048
31049 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31050 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
31051 };
31052
31053 var getElementBox = function getElementBox(ele) {
31054 ele.boundingBox();
31055 return ele[0]._private.bodyBounds;
31056 };
31057
31058 var getLabelBox = function getLabelBox(ele) {
31059 ele.boundingBox();
31060 return ele[0]._private.labelBounds.main || emptyBb;
31061 };
31062
31063 var getSourceLabelBox = function getSourceLabelBox(ele) {
31064 ele.boundingBox();
31065 return ele[0]._private.labelBounds.source || emptyBb;
31066 };
31067
31068 var getTargetLabelBox = function getTargetLabelBox(ele) {
31069 ele.boundingBox();
31070 return ele[0]._private.labelBounds.target || emptyBb;
31071 };
31072
31073 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31074 return scaledLabelShown;
31075 };
31076
31077 var getElementRotationPoint = function getElementRotationPoint(ele) {
31078 return getBoxCenter(getElementBox(ele));
31079 };
31080
31081 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31082 var pre = prefix ? prefix + '-' : '';
31083 return {
31084 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31085 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31086 };
31087 };
31088
31089 var getRsPt = function getRsPt(ele, x, y) {
31090 var rs = ele[0]._private.rscratch;
31091 return {
31092 x: rs[x],
31093 y: rs[y]
31094 };
31095 };
31096
31097 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31098 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31099 };
31100
31101 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31102 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31103 };
31104
31105 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31106 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31107 };
31108
31109 var getElementRotationOffset = function getElementRotationOffset(ele) {
31110 return getCenterOffset(getElementBox(ele));
31111 };
31112
31113 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31114 return getCenterOffset(getSourceLabelBox(ele));
31115 };
31116
31117 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31118 return getCenterOffset(getTargetLabelBox(ele));
31119 };
31120
31121 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31122 var bb = getLabelBox(ele);
31123 var p = getCenterOffset(getLabelBox(ele));
31124
31125 if (ele.isNode()) {
31126 switch (ele.pstyle('text-halign').value) {
31127 case 'left':
31128 p.x = -bb.w;
31129 break;
31130
31131 case 'right':
31132 p.x = 0;
31133 break;
31134 }
31135
31136 switch (ele.pstyle('text-valign').value) {
31137 case 'top':
31138 p.y = -bb.h;
31139 break;
31140
31141 case 'bottom':
31142 p.y = 0;
31143 break;
31144 }
31145 }
31146
31147 return p;
31148 };
31149
31150 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31151 getKey: getStyleKey,
31152 doesEleInvalidateKey: backgroundTimestampHasChanged,
31153 drawElement: drawElement,
31154 getBoundingBox: getElementBox,
31155 getRotationPoint: getElementRotationPoint,
31156 getRotationOffset: getElementRotationOffset,
31157 allowEdgeTxrCaching: false,
31158 allowParentTxrCaching: false
31159 });
31160 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31161 getKey: getLabelKey,
31162 drawElement: drawLabel,
31163 getBoundingBox: getLabelBox,
31164 getRotationPoint: getLabelRotationPoint,
31165 getRotationOffset: getLabelRotationOffset,
31166 isVisible: isLabelVisibleAtScale
31167 });
31168 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31169 getKey: getSourceLabelKey,
31170 drawElement: drawSourceLabel,
31171 getBoundingBox: getSourceLabelBox,
31172 getRotationPoint: getSourceLabelRotationPoint,
31173 getRotationOffset: getSourceLabelRotationOffset,
31174 isVisible: isLabelVisibleAtScale
31175 });
31176 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31177 getKey: getTargetLabelKey,
31178 drawElement: drawTargetLabel,
31179 getBoundingBox: getTargetLabelBox,
31180 getRotationPoint: getTargetLabelRotationPoint,
31181 getRotationOffset: getTargetLabelRotationOffset,
31182 isVisible: isLabelVisibleAtScale
31183 });
31184 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31185 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31186 // each cache should check for sub-key diff to see that the update affects that cache particularly
31187 eleTxrCache.invalidateElements(eles);
31188 lblTxrCache.invalidateElements(eles);
31189 slbTxrCache.invalidateElements(eles);
31190 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31191
31192 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31193
31194 for (var _i = 0; _i < eles.length; _i++) {
31195 var _p = eles[_i]._private;
31196 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31197 }
31198 });
31199
31200 var refineInLayers = function refineInLayers(reqs) {
31201 for (var i = 0; i < reqs.length; i++) {
31202 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31203 }
31204 };
31205
31206 eleTxrCache.onDequeue(refineInLayers);
31207 lblTxrCache.onDequeue(refineInLayers);
31208 slbTxrCache.onDequeue(refineInLayers);
31209 tlbTxrCache.onDequeue(refineInLayers);
31210}
31211
31212CRp$a.redrawHint = function (group, bool) {
31213 var r = this;
31214
31215 switch (group) {
31216 case 'eles':
31217 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31218 break;
31219
31220 case 'drag':
31221 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31222 break;
31223
31224 case 'select':
31225 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31226 break;
31227 }
31228}; // whether to use Path2D caching for drawing
31229
31230
31231var pathsImpld = typeof Path2D !== 'undefined';
31232
31233CRp$a.path2dEnabled = function (on) {
31234 if (on === undefined) {
31235 return this.pathsEnabled;
31236 }
31237
31238 this.pathsEnabled = on ? true : false;
31239};
31240
31241CRp$a.usePaths = function () {
31242 return pathsImpld && this.pathsEnabled;
31243};
31244
31245CRp$a.setImgSmoothing = function (context, bool) {
31246 if (context.imageSmoothingEnabled != null) {
31247 context.imageSmoothingEnabled = bool;
31248 } else {
31249 context.webkitImageSmoothingEnabled = bool;
31250 context.mozImageSmoothingEnabled = bool;
31251 context.msImageSmoothingEnabled = bool;
31252 }
31253};
31254
31255CRp$a.getImgSmoothing = function (context) {
31256 if (context.imageSmoothingEnabled != null) {
31257 return context.imageSmoothingEnabled;
31258 } else {
31259 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31260 }
31261};
31262
31263CRp$a.makeOffscreenCanvas = function (width, height) {
31264 var canvas;
31265
31266 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
31267 canvas = new OffscreenCanvas(width, height);
31268 } else {
31269 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31270
31271 canvas.width = width;
31272 canvas.height = height;
31273 }
31274
31275 return canvas;
31276};
31277
31278[CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
31279 extend(CRp$a, props);
31280});
31281
31282var renderer = [{
31283 name: 'null',
31284 impl: NullRenderer
31285}, {
31286 name: 'base',
31287 impl: BR
31288}, {
31289 name: 'canvas',
31290 impl: CR
31291}];
31292
31293var incExts = [{
31294 type: 'layout',
31295 extensions: layout
31296}, {
31297 type: 'renderer',
31298 extensions: renderer
31299}];
31300
31301var extensions = {}; // registered modules for extensions, indexed by name
31302
31303var modules = {};
31304
31305function setExtension(type, name, registrant) {
31306 var ext = registrant;
31307
31308 var overrideErr = function overrideErr(field) {
31309 error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31310 };
31311
31312 if (type === 'core') {
31313 if (Core.prototype[name]) {
31314 return overrideErr(name);
31315 } else {
31316 Core.prototype[name] = registrant;
31317 }
31318 } else if (type === 'collection') {
31319 if (Collection.prototype[name]) {
31320 return overrideErr(name);
31321 } else {
31322 Collection.prototype[name] = registrant;
31323 }
31324 } else if (type === 'layout') {
31325 // fill in missing layout functions in the prototype
31326 var Layout = function Layout(options) {
31327 this.options = options;
31328 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31329
31330 if (!plainObject(this._private)) {
31331 this._private = {};
31332 }
31333
31334 this._private.cy = options.cy;
31335 this._private.listeners = [];
31336 this.createEmitter();
31337 };
31338
31339 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31340 var optLayoutFns = [];
31341
31342 for (var i = 0; i < optLayoutFns.length; i++) {
31343 var fnName = optLayoutFns[i];
31344
31345 layoutProto[fnName] = layoutProto[fnName] || function () {
31346 return this;
31347 };
31348 } // either .start() or .run() is defined, so autogen the other
31349
31350
31351 if (layoutProto.start && !layoutProto.run) {
31352 layoutProto.run = function () {
31353 this.start();
31354 return this;
31355 };
31356 } else if (!layoutProto.start && layoutProto.run) {
31357 layoutProto.start = function () {
31358 this.run();
31359 return this;
31360 };
31361 }
31362
31363 var regStop = registrant.prototype.stop;
31364
31365 layoutProto.stop = function () {
31366 var opts = this.options;
31367
31368 if (opts && opts.animate) {
31369 var anis = this.animations;
31370
31371 if (anis) {
31372 for (var _i = 0; _i < anis.length; _i++) {
31373 anis[_i].stop();
31374 }
31375 }
31376 }
31377
31378 if (regStop) {
31379 regStop.call(this);
31380 } else {
31381 this.emit('layoutstop');
31382 }
31383
31384 return this;
31385 };
31386
31387 if (!layoutProto.destroy) {
31388 layoutProto.destroy = function () {
31389 return this;
31390 };
31391 }
31392
31393 layoutProto.cy = function () {
31394 return this._private.cy;
31395 };
31396
31397 var getCy = function getCy(layout) {
31398 return layout._private.cy;
31399 };
31400
31401 var emitterOpts = {
31402 addEventFields: function addEventFields(layout, evt) {
31403 evt.layout = layout;
31404 evt.cy = getCy(layout);
31405 evt.target = layout;
31406 },
31407 bubble: function bubble() {
31408 return true;
31409 },
31410 parent: function parent(layout) {
31411 return getCy(layout);
31412 }
31413 };
31414 extend(layoutProto, {
31415 createEmitter: function createEmitter() {
31416 this._private.emitter = new Emitter(emitterOpts, this);
31417 return this;
31418 },
31419 emitter: function emitter() {
31420 return this._private.emitter;
31421 },
31422 on: function on(evt, cb) {
31423 this.emitter().on(evt, cb);
31424 return this;
31425 },
31426 one: function one(evt, cb) {
31427 this.emitter().one(evt, cb);
31428 return this;
31429 },
31430 once: function once(evt, cb) {
31431 this.emitter().one(evt, cb);
31432 return this;
31433 },
31434 removeListener: function removeListener(evt, cb) {
31435 this.emitter().removeListener(evt, cb);
31436 return this;
31437 },
31438 removeAllListeners: function removeAllListeners() {
31439 this.emitter().removeAllListeners();
31440 return this;
31441 },
31442 emit: function emit(evt, params) {
31443 this.emitter().emit(evt, params);
31444 return this;
31445 }
31446 });
31447 define$3.eventAliasesOn(layoutProto);
31448 ext = Layout; // replace with our wrapped layout
31449 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31450 // user registered renderers inherit from base
31451 var BaseRenderer = getExtension('renderer', 'base');
31452 var bProto = BaseRenderer.prototype;
31453 var RegistrantRenderer = registrant;
31454 var rProto = registrant.prototype;
31455
31456 var Renderer = function Renderer() {
31457 BaseRenderer.apply(this, arguments);
31458 RegistrantRenderer.apply(this, arguments);
31459 };
31460
31461 var proto = Renderer.prototype;
31462
31463 for (var pName in bProto) {
31464 var pVal = bProto[pName];
31465 var existsInR = rProto[pName] != null;
31466
31467 if (existsInR) {
31468 return overrideErr(pName);
31469 }
31470
31471 proto[pName] = pVal; // take impl from base
31472 }
31473
31474 for (var _pName in rProto) {
31475 proto[_pName] = rProto[_pName]; // take impl from registrant
31476 }
31477
31478 bProto.clientFunctions.forEach(function (name) {
31479 proto[name] = proto[name] || function () {
31480 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31481 };
31482 });
31483 ext = Renderer;
31484 }
31485
31486 return setMap({
31487 map: extensions,
31488 keys: [type, name],
31489 value: ext
31490 });
31491}
31492
31493function getExtension(type, name) {
31494 return getMap({
31495 map: extensions,
31496 keys: [type, name]
31497 });
31498}
31499
31500function setModule(type, name, moduleType, moduleName, registrant) {
31501 return setMap({
31502 map: modules,
31503 keys: [type, name, moduleType, moduleName],
31504 value: registrant
31505 });
31506}
31507
31508function getModule(type, name, moduleType, moduleName) {
31509 return getMap({
31510 map: modules,
31511 keys: [type, name, moduleType, moduleName]
31512 });
31513}
31514
31515var extension = function extension() {
31516 // e.g. extension('renderer', 'svg')
31517 if (arguments.length === 2) {
31518 return getExtension.apply(null, arguments);
31519 } // e.g. extension('renderer', 'svg', { ... })
31520 else if (arguments.length === 3) {
31521 return setExtension.apply(null, arguments);
31522 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31523 else if (arguments.length === 4) {
31524 return getModule.apply(null, arguments);
31525 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31526 else if (arguments.length === 5) {
31527 return setModule.apply(null, arguments);
31528 } else {
31529 error('Invalid extension access syntax');
31530 }
31531}; // allows a core instance to access extensions internally
31532
31533
31534Core.prototype.extension = extension; // included extensions
31535
31536incExts.forEach(function (group) {
31537 group.extensions.forEach(function (ext) {
31538 setExtension(group.type, ext.name, ext.impl);
31539 });
31540});
31541
31542// (useful for init)
31543
31544var Stylesheet = function Stylesheet() {
31545 if (!(this instanceof Stylesheet)) {
31546 return new Stylesheet();
31547 }
31548
31549 this.length = 0;
31550};
31551
31552var sheetfn = Stylesheet.prototype;
31553
31554sheetfn.instanceString = function () {
31555 return 'stylesheet';
31556}; // just store the selector to be parsed later
31557
31558
31559sheetfn.selector = function (selector) {
31560 var i = this.length++;
31561 this[i] = {
31562 selector: selector,
31563 properties: []
31564 };
31565 return this; // chaining
31566}; // just store the property to be parsed later
31567
31568
31569sheetfn.css = function (name, value) {
31570 var i = this.length - 1;
31571
31572 if (string(name)) {
31573 this[i].properties.push({
31574 name: name,
31575 value: value
31576 });
31577 } else if (plainObject(name)) {
31578 var map = name;
31579 var propNames = Object.keys(map);
31580
31581 for (var j = 0; j < propNames.length; j++) {
31582 var key = propNames[j];
31583 var mapVal = map[key];
31584
31585 if (mapVal == null) {
31586 continue;
31587 }
31588
31589 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31590
31591 if (prop == null) {
31592 continue;
31593 }
31594
31595 var _name = prop.name;
31596 var _value = mapVal;
31597 this[i].properties.push({
31598 name: _name,
31599 value: _value
31600 });
31601 }
31602 }
31603
31604 return this; // chaining
31605};
31606
31607sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31608
31609sheetfn.generateStyle = function (cy) {
31610 var style = new Style(cy);
31611 return this.appendToStyle(style);
31612}; // append a dummy stylesheet object on a real style object
31613
31614
31615sheetfn.appendToStyle = function (style) {
31616 for (var i = 0; i < this.length; i++) {
31617 var context = this[i];
31618 var selector = context.selector;
31619 var props = context.properties;
31620 style.selector(selector); // apply selector
31621
31622 for (var j = 0; j < props.length; j++) {
31623 var prop = props[j];
31624 style.css(prop.name, prop.value); // apply property
31625 }
31626 }
31627
31628 return style;
31629};
31630
31631var version = "3.18.2";
31632
31633var cytoscape = function cytoscape(options) {
31634 // if no options specified, use default
31635 if (options === undefined) {
31636 options = {};
31637 } // create instance
31638
31639
31640 if (plainObject(options)) {
31641 return new Core(options);
31642 } // allow for registration of extensions
31643 else if (string(options)) {
31644 return extension.apply(extension, arguments);
31645 }
31646}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31647
31648
31649cytoscape.use = function (ext) {
31650 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31651
31652 args.unshift(cytoscape); // cytoscape is first arg to ext
31653
31654 ext.apply(null, args);
31655 return this;
31656};
31657
31658cytoscape.warnings = function (bool) {
31659 return warnings(bool);
31660}; // replaced by build system
31661
31662
31663cytoscape.version = version; // expose public apis (mostly for extensions)
31664
31665cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31666
31667export default cytoscape;