UNPKG

857 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2020, 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 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_SEED = 5381;
707var hashIterableInts = function hashIterableInts(iterator) {
708 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
709 // djb2/string-hash
710 var hash = seed;
711 var entry;
712
713 for (;;) {
714 entry = iterator.next();
715
716 if (entry.done) {
717 break;
718 }
719
720 hash = (hash << 5) + hash + entry.value | 0;
721 }
722
723 return hash;
724};
725var hashInt = function hashInt(num) {
726 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
727 // djb2/string-hash
728 return (seed << 5) + seed + num | 0;
729};
730var hashIntsArray = function hashIntsArray(ints, seed) {
731 var entry = {
732 value: 0,
733 done: false
734 };
735 var i = 0;
736 var length = ints.length;
737 var iterator = {
738 next: function next() {
739 if (i < length) {
740 entry.value = ints[i++];
741 } else {
742 entry.done = true;
743 }
744
745 return entry;
746 }
747 };
748 return hashIterableInts(iterator, seed);
749};
750var hashString = function hashString(str, seed) {
751 var entry = {
752 value: 0,
753 done: false
754 };
755 var i = 0;
756 var length = str.length;
757 var iterator = {
758 next: function next() {
759 if (i < length) {
760 entry.value = str.charCodeAt(i++);
761 } else {
762 entry.done = true;
763 }
764
765 return entry;
766 }
767 };
768 return hashIterableInts(iterator, seed);
769};
770var hashStrings = function hashStrings() {
771 return hashStringsArray(arguments);
772};
773var hashStringsArray = function hashStringsArray(strs) {
774 var hash;
775
776 for (var i = 0; i < strs.length; i++) {
777 var str = strs[i];
778
779 if (i === 0) {
780 hash = hashString(str);
781 } else {
782 hash = hashString(str, hash);
783 }
784 }
785
786 return hash;
787};
788
789/*global console */
790var warningsEnabled = true;
791var warnSupported = console.warn != null; // eslint-disable-line no-console
792
793var traceSupported = console.trace != null; // eslint-disable-line no-console
794
795var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
796var trueify = function trueify() {
797 return true;
798};
799var falsify = function falsify() {
800 return false;
801};
802var zeroify = function zeroify() {
803 return 0;
804};
805var noop = function noop() {};
806var error = function error(msg) {
807 throw new Error(msg);
808};
809var warnings = function warnings(enabled) {
810 if (enabled !== undefined) {
811 warningsEnabled = !!enabled;
812 } else {
813 return warningsEnabled;
814 }
815};
816var warn = function warn(msg) {
817 /* eslint-disable no-console */
818 if (!warnings()) {
819 return;
820 }
821
822 if (warnSupported) {
823 console.warn(msg);
824 } else {
825 console.log(msg);
826
827 if (traceSupported) {
828 console.trace();
829 }
830 }
831};
832/* eslint-enable */
833
834var clone = function clone(obj) {
835 return extend({}, obj);
836}; // gets a shallow copy of the argument
837
838var copy = function copy(obj) {
839 if (obj == null) {
840 return obj;
841 }
842
843 if (array(obj)) {
844 return obj.slice();
845 } else if (plainObject(obj)) {
846 return clone(obj);
847 } else {
848 return obj;
849 }
850};
851var copyArray = function copyArray(arr) {
852 return arr.slice();
853};
854var uuid = function uuid(a, b
855/* placeholders */
856) {
857 for ( // loop :)
858 b = a = ''; // b - result , a - numeric letiable
859 a++ < 36; //
860 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
861 ? // return a random number or 4
862 (a ^ 15 // if "a" is not 15
863 ? // genetate a random number from 0 to 15
864 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
865 : 4 // otherwise 4
866 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
867 ) {
868 }
869
870 return b;
871};
872var _staticEmptyObject = {};
873var staticEmptyObject = function staticEmptyObject() {
874 return _staticEmptyObject;
875};
876var defaults = function defaults(_defaults) {
877 var keys = Object.keys(_defaults);
878 return function (opts) {
879 var filledOpts = {};
880
881 for (var i = 0; i < keys.length; i++) {
882 var key = keys[i];
883 var optVal = opts == null ? undefined : opts[key];
884 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
885 }
886
887 return filledOpts;
888 };
889};
890var removeFromArray = function removeFromArray(arr, ele, manyCopies) {
891 for (var i = arr.length; i >= 0; i--) {
892 if (arr[i] === ele) {
893 arr.splice(i, 1);
894
895 if (!manyCopies) {
896 break;
897 }
898 }
899 }
900};
901var clearArray = function clearArray(arr) {
902 arr.splice(0, arr.length);
903};
904var push = function push(arr, otherArr) {
905 for (var i = 0; i < otherArr.length; i++) {
906 var el = otherArr[i];
907 arr.push(el);
908 }
909};
910var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
911 if (prefix) {
912 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
913 }
914
915 return obj[propName];
916};
917var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
918 if (prefix) {
919 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
920 }
921
922 obj[propName] = value;
923};
924
925/* global Map */
926var ObjectMap =
927/*#__PURE__*/
928function () {
929 function ObjectMap() {
930 _classCallCheck(this, ObjectMap);
931
932 this._obj = {};
933 }
934
935 _createClass(ObjectMap, [{
936 key: "set",
937 value: function set(key, val) {
938 this._obj[key] = val;
939 return this;
940 }
941 }, {
942 key: "delete",
943 value: function _delete(key) {
944 this._obj[key] = undefined;
945 return this;
946 }
947 }, {
948 key: "clear",
949 value: function clear() {
950 this._obj = {};
951 }
952 }, {
953 key: "has",
954 value: function has(key) {
955 return this._obj[key] !== undefined;
956 }
957 }, {
958 key: "get",
959 value: function get(key) {
960 return this._obj[key];
961 }
962 }]);
963
964 return ObjectMap;
965}();
966
967var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
968
969/* global Set */
970var undef = "undefined" ;
971
972var ObjectSet =
973/*#__PURE__*/
974function () {
975 function ObjectSet(arrayOrObjectSet) {
976 _classCallCheck(this, ObjectSet);
977
978 this._obj = Object.create(null);
979 this.size = 0;
980
981 if (arrayOrObjectSet != null) {
982 var arr;
983
984 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
985 arr = arrayOrObjectSet.toArray();
986 } else {
987 arr = arrayOrObjectSet;
988 }
989
990 for (var i = 0; i < arr.length; i++) {
991 this.add(arr[i]);
992 }
993 }
994 }
995
996 _createClass(ObjectSet, [{
997 key: "instanceString",
998 value: function instanceString() {
999 return 'set';
1000 }
1001 }, {
1002 key: "add",
1003 value: function add(val) {
1004 var o = this._obj;
1005
1006 if (o[val] !== 1) {
1007 o[val] = 1;
1008 this.size++;
1009 }
1010 }
1011 }, {
1012 key: "delete",
1013 value: function _delete(val) {
1014 var o = this._obj;
1015
1016 if (o[val] === 1) {
1017 o[val] = 0;
1018 this.size--;
1019 }
1020 }
1021 }, {
1022 key: "clear",
1023 value: function clear() {
1024 this._obj = Object.create(null);
1025 }
1026 }, {
1027 key: "has",
1028 value: function has(val) {
1029 return this._obj[val] === 1;
1030 }
1031 }, {
1032 key: "toArray",
1033 value: function toArray() {
1034 var _this = this;
1035
1036 return Object.keys(this._obj).filter(function (key) {
1037 return _this.has(key);
1038 });
1039 }
1040 }, {
1041 key: "forEach",
1042 value: function forEach(callback, thisArg) {
1043 return this.toArray().forEach(callback, thisArg);
1044 }
1045 }]);
1046
1047 return ObjectSet;
1048}();
1049
1050var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1051
1052var Element = function Element(cy, params, restore) {
1053 restore = restore === undefined || restore ? true : false;
1054
1055 if (cy === undefined || params === undefined || !core(cy)) {
1056 error('An element must have a core reference and parameters set');
1057 return;
1058 }
1059
1060 var group = params.group; // try to automatically infer the group if unspecified
1061
1062 if (group == null) {
1063 if (params.data && params.data.source != null && params.data.target != null) {
1064 group = 'edges';
1065 } else {
1066 group = 'nodes';
1067 }
1068 } // validate group
1069
1070
1071 if (group !== 'nodes' && group !== 'edges') {
1072 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1073 return;
1074 } // make the element array-like, just like a collection
1075
1076
1077 this.length = 1;
1078 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1079
1080 var _p = this._private = {
1081 cy: cy,
1082 single: true,
1083 // indicates this is an element
1084 data: params.data || {},
1085 // data object
1086 position: params.position || {
1087 x: 0,
1088 y: 0
1089 },
1090 // (x, y) position pair
1091 autoWidth: undefined,
1092 // width and height of nodes calculated by the renderer when set to special 'auto' value
1093 autoHeight: undefined,
1094 autoPadding: undefined,
1095 compoundBoundsClean: false,
1096 // whether the compound dimensions need to be recalculated the next time dimensions are read
1097 listeners: [],
1098 // array of bound listeners
1099 group: group,
1100 // string; 'nodes' or 'edges'
1101 style: {},
1102 // properties as set by the style
1103 rstyle: {},
1104 // properties for style sent from the renderer to the core
1105 styleCxts: [],
1106 // applied style contexts from the styler
1107 styleKeys: {},
1108 // per-group keys of style property values
1109 removed: true,
1110 // whether it's inside the vis; true if removed (set true here since we call restore)
1111 selected: params.selected ? true : false,
1112 // whether it's selected
1113 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1114 // whether it's selectable
1115 locked: params.locked ? true : false,
1116 // whether the element is locked (cannot be moved)
1117 grabbed: false,
1118 // whether the element is grabbed by the mouse; renderer sets this privately
1119 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1120 // whether the element can be grabbed
1121 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1122 // whether the element has passthrough panning enabled
1123 active: false,
1124 // whether the element is active from user interaction
1125 classes: new Set$1(),
1126 // map ( className => true )
1127 animation: {
1128 // object for currently-running animations
1129 current: [],
1130 queue: []
1131 },
1132 rscratch: {},
1133 // object in which the renderer can store information
1134 scratch: params.scratch || {},
1135 // scratch objects
1136 edges: [],
1137 // array of connected edges
1138 children: [],
1139 // array of children
1140 parent: null,
1141 // parent ref
1142 traversalCache: {},
1143 // cache of output of traversal functions
1144 backgrounding: false,
1145 // whether background images are loading
1146 bbCache: null,
1147 // cache of the current bounding box
1148 bbCacheShift: {
1149 x: 0,
1150 y: 0
1151 },
1152 // shift applied to cached bb to be applied on next get
1153 bodyBounds: null,
1154 // bounds cache of element body, w/o overlay
1155 overlayBounds: null,
1156 // bounds cache of element body, including overlay
1157 labelBounds: {
1158 // bounds cache of labels
1159 all: null,
1160 source: null,
1161 target: null,
1162 main: null
1163 },
1164 arrowBounds: {
1165 // bounds cache of edge arrows
1166 source: null,
1167 target: null,
1168 'mid-source': null,
1169 'mid-target': null
1170 }
1171 };
1172
1173 if (_p.position.x == null) {
1174 _p.position.x = 0;
1175 }
1176
1177 if (_p.position.y == null) {
1178 _p.position.y = 0;
1179 } // renderedPosition overrides if specified
1180
1181
1182 if (params.renderedPosition) {
1183 var rpos = params.renderedPosition;
1184 var pan = cy.pan();
1185 var zoom = cy.zoom();
1186 _p.position = {
1187 x: (rpos.x - pan.x) / zoom,
1188 y: (rpos.y - pan.y) / zoom
1189 };
1190 }
1191
1192 var classes = [];
1193
1194 if (array(params.classes)) {
1195 classes = params.classes;
1196 } else if (string(params.classes)) {
1197 classes = params.classes.split(/\s+/);
1198 }
1199
1200 for (var i = 0, l = classes.length; i < l; i++) {
1201 var cls = classes[i];
1202
1203 if (!cls || cls === '') {
1204 continue;
1205 }
1206
1207 _p.classes.add(cls);
1208 }
1209
1210 this.createEmitter();
1211 var bypass = params.style || params.css;
1212
1213 if (bypass) {
1214 warn('Setting a `style` bypass at element creation is deprecated');
1215 this.style(bypass);
1216 }
1217
1218 if (restore === undefined || restore) {
1219 this.restore();
1220 }
1221};
1222
1223var defineSearch = function defineSearch(params) {
1224 params = {
1225 bfs: params.bfs || !params.dfs,
1226 dfs: params.dfs || !params.bfs
1227 }; // from pseudocode on wikipedia
1228
1229 return function searchFn(roots, fn$1, directed) {
1230 var options;
1231
1232 if (plainObject(roots) && !elementOrCollection(roots)) {
1233 options = roots;
1234 roots = options.roots || options.root;
1235 fn$1 = options.visit;
1236 directed = options.directed;
1237 }
1238
1239 directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
1240 fn$1 = fn(fn$1) ? fn$1 : function () {};
1241 var cy = this._private.cy;
1242 var v = roots = string(roots) ? this.filter(roots) : roots;
1243 var Q = [];
1244 var connectedNodes = [];
1245 var connectedBy = {};
1246 var id2depth = {};
1247 var V = {};
1248 var j = 0;
1249 var found;
1250
1251 var _this$byGroup = this.byGroup(),
1252 nodes = _this$byGroup.nodes,
1253 edges = _this$byGroup.edges; // enqueue v
1254
1255
1256 for (var i = 0; i < v.length; i++) {
1257 var vi = v[i];
1258 var viId = vi.id();
1259
1260 if (vi.isNode()) {
1261 Q.unshift(vi);
1262
1263 if (params.bfs) {
1264 V[viId] = true;
1265 connectedNodes.push(vi);
1266 }
1267
1268 id2depth[viId] = 0;
1269 }
1270 }
1271
1272 var _loop2 = function _loop2() {
1273 var v = params.bfs ? Q.shift() : Q.pop();
1274 var vId = v.id();
1275
1276 if (params.dfs) {
1277 if (V[vId]) {
1278 return "continue";
1279 }
1280
1281 V[vId] = true;
1282 connectedNodes.push(v);
1283 }
1284
1285 var depth = id2depth[vId];
1286 var prevEdge = connectedBy[vId];
1287 var src = prevEdge != null ? prevEdge.source() : null;
1288 var tgt = prevEdge != null ? prevEdge.target() : null;
1289 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1290 var ret = void 0;
1291 ret = fn$1(v, prevEdge, prevNode, j++, depth);
1292
1293 if (ret === true) {
1294 found = v;
1295 return "break";
1296 }
1297
1298 if (ret === false) {
1299 return "break";
1300 }
1301
1302 var vwEdges = v.connectedEdges().filter(function (e) {
1303 return (!directed || e.source().same(v)) && edges.has(e);
1304 });
1305
1306 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1307 var e = vwEdges[_i2];
1308 var w = e.connectedNodes().filter(function (n) {
1309 return !n.same(v) && nodes.has(n);
1310 });
1311 var wId = w.id();
1312
1313 if (w.length !== 0 && !V[wId]) {
1314 w = w[0];
1315 Q.push(w);
1316
1317 if (params.bfs) {
1318 V[wId] = true;
1319 connectedNodes.push(w);
1320 }
1321
1322 connectedBy[wId] = e;
1323 id2depth[wId] = id2depth[vId] + 1;
1324 }
1325 }
1326 };
1327
1328 _loop: while (Q.length !== 0) {
1329 var _ret = _loop2();
1330
1331 switch (_ret) {
1332 case "continue":
1333 continue;
1334
1335 case "break":
1336 break _loop;
1337 }
1338 }
1339
1340 var connectedEles = cy.collection();
1341
1342 for (var _i = 0; _i < connectedNodes.length; _i++) {
1343 var node = connectedNodes[_i];
1344 var edge = connectedBy[node.id()];
1345
1346 if (edge != null) {
1347 connectedEles.merge(edge);
1348 }
1349
1350 connectedEles.merge(node);
1351 }
1352
1353 return {
1354 path: cy.collection(connectedEles),
1355 found: cy.collection(found)
1356 };
1357 };
1358}; // search, spanning trees, etc
1359
1360
1361var elesfn = {
1362 breadthFirstSearch: defineSearch({
1363 bfs: true
1364 }),
1365 depthFirstSearch: defineSearch({
1366 dfs: true
1367 })
1368}; // nice, short mathemathical alias
1369
1370elesfn.bfs = elesfn.breadthFirstSearch;
1371elesfn.dfs = elesfn.depthFirstSearch;
1372
1373var dijkstraDefaults = defaults({
1374 root: null,
1375 weight: function weight(edge) {
1376 return 1;
1377 },
1378 directed: false
1379});
1380var elesfn$1 = {
1381 dijkstra: function dijkstra(options) {
1382 if (!plainObject(options)) {
1383 var args = arguments;
1384 options = {
1385 root: args[0],
1386 weight: args[1],
1387 directed: args[2]
1388 };
1389 }
1390
1391 var _dijkstraDefaults = dijkstraDefaults(options),
1392 root = _dijkstraDefaults.root,
1393 weight = _dijkstraDefaults.weight,
1394 directed = _dijkstraDefaults.directed;
1395
1396 var eles = this;
1397 var weightFn = weight;
1398 var source = string(root) ? this.filter(root)[0] : root[0];
1399 var dist = {};
1400 var prev = {};
1401 var knownDist = {};
1402
1403 var _this$byGroup = this.byGroup(),
1404 nodes = _this$byGroup.nodes,
1405 edges = _this$byGroup.edges;
1406
1407 edges.unmergeBy(function (ele) {
1408 return ele.isLoop();
1409 });
1410
1411 var getDist = function getDist(node) {
1412 return dist[node.id()];
1413 };
1414
1415 var setDist = function setDist(node, d) {
1416 dist[node.id()] = d;
1417 Q.updateItem(node);
1418 };
1419
1420 var Q = new Heap(function (a, b) {
1421 return getDist(a) - getDist(b);
1422 });
1423
1424 for (var i = 0; i < nodes.length; i++) {
1425 var node = nodes[i];
1426 dist[node.id()] = node.same(source) ? 0 : Infinity;
1427 Q.push(node);
1428 }
1429
1430 var distBetween = function distBetween(u, v) {
1431 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
1432 var smallestDistance = Infinity;
1433 var smallestEdge;
1434
1435 for (var _i = 0; _i < uvs.length; _i++) {
1436 var edge = uvs[_i];
1437
1438 var _weight = weightFn(edge);
1439
1440 if (_weight < smallestDistance || !smallestEdge) {
1441 smallestDistance = _weight;
1442 smallestEdge = edge;
1443 }
1444 }
1445
1446 return {
1447 edge: smallestEdge,
1448 dist: smallestDistance
1449 };
1450 };
1451
1452 while (Q.size() > 0) {
1453 var u = Q.pop();
1454 var smalletsDist = getDist(u);
1455 var uid = u.id();
1456 knownDist[uid] = smalletsDist;
1457
1458 if (smalletsDist === Infinity) {
1459 continue;
1460 }
1461
1462 var neighbors = u.neighborhood().intersect(nodes);
1463
1464 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
1465 var v = neighbors[_i2];
1466 var vid = v.id();
1467 var vDist = distBetween(u, v);
1468 var alt = smalletsDist + vDist.dist;
1469
1470 if (alt < getDist(v)) {
1471 setDist(v, alt);
1472 prev[vid] = {
1473 node: u,
1474 edge: vDist.edge
1475 };
1476 }
1477 } // for
1478
1479 } // while
1480
1481
1482 return {
1483 distanceTo: function distanceTo(node) {
1484 var target = string(node) ? nodes.filter(node)[0] : node[0];
1485 return knownDist[target.id()];
1486 },
1487 pathTo: function pathTo(node) {
1488 var target = string(node) ? nodes.filter(node)[0] : node[0];
1489 var S = [];
1490 var u = target;
1491 var uid = u.id();
1492
1493 if (target.length > 0) {
1494 S.unshift(target);
1495
1496 while (prev[uid]) {
1497 var p = prev[uid];
1498 S.unshift(p.edge);
1499 S.unshift(p.node);
1500 u = p.node;
1501 uid = u.id();
1502 }
1503 }
1504
1505 return eles.spawn(S);
1506 }
1507 };
1508 }
1509};
1510
1511var elesfn$2 = {
1512 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
1513 // implemented from pseudocode from wikipedia
1514 kruskal: function kruskal(weightFn) {
1515 weightFn = weightFn || function (edge) {
1516 return 1;
1517 };
1518
1519 var _this$byGroup = this.byGroup(),
1520 nodes = _this$byGroup.nodes,
1521 edges = _this$byGroup.edges;
1522
1523 var numNodes = nodes.length;
1524 var forest = new Array(numNodes);
1525 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
1526
1527 var findSetIndex = function findSetIndex(ele) {
1528 for (var i = 0; i < forest.length; i++) {
1529 var eles = forest[i];
1530
1531 if (eles.has(ele)) {
1532 return i;
1533 }
1534 }
1535 }; // start with one forest per node
1536
1537
1538 for (var i = 0; i < numNodes; i++) {
1539 forest[i] = this.spawn(nodes[i]);
1540 }
1541
1542 var S = edges.sort(function (a, b) {
1543 return weightFn(a) - weightFn(b);
1544 });
1545
1546 for (var _i = 0; _i < S.length; _i++) {
1547 var edge = S[_i];
1548 var u = edge.source()[0];
1549 var v = edge.target()[0];
1550 var setUIndex = findSetIndex(u);
1551 var setVIndex = findSetIndex(v);
1552 var setU = forest[setUIndex];
1553 var setV = forest[setVIndex];
1554
1555 if (setUIndex !== setVIndex) {
1556 A.merge(edge); // combine forests for u and v
1557
1558 setU.merge(setV);
1559 forest.splice(setVIndex, 1);
1560 }
1561 }
1562
1563 return A;
1564 }
1565};
1566
1567var aStarDefaults = defaults({
1568 root: null,
1569 goal: null,
1570 weight: function weight(edge) {
1571 return 1;
1572 },
1573 heuristic: function heuristic(edge) {
1574 return 0;
1575 },
1576 directed: false
1577});
1578var elesfn$3 = {
1579 // Implemented from pseudocode from wikipedia
1580 aStar: function aStar(options) {
1581 var cy = this.cy();
1582
1583 var _aStarDefaults = aStarDefaults(options),
1584 root = _aStarDefaults.root,
1585 goal = _aStarDefaults.goal,
1586 heuristic = _aStarDefaults.heuristic,
1587 directed = _aStarDefaults.directed,
1588 weight = _aStarDefaults.weight;
1589
1590 root = cy.collection(root)[0];
1591 goal = cy.collection(goal)[0];
1592 var sid = root.id();
1593 var tid = goal.id();
1594 var gScore = {};
1595 var fScore = {};
1596 var closedSetIds = {};
1597 var openSet = new Heap(function (a, b) {
1598 return fScore[a.id()] - fScore[b.id()];
1599 });
1600 var openSetIds = new Set$1();
1601 var cameFrom = {};
1602 var cameFromEdge = {};
1603
1604 var addToOpenSet = function addToOpenSet(ele, id) {
1605 openSet.push(ele);
1606 openSetIds.add(id);
1607 };
1608
1609 var cMin, cMinId;
1610
1611 var popFromOpenSet = function popFromOpenSet() {
1612 cMin = openSet.pop();
1613 cMinId = cMin.id();
1614 openSetIds["delete"](cMinId);
1615 };
1616
1617 var isInOpenSet = function isInOpenSet(id) {
1618 return openSetIds.has(id);
1619 };
1620
1621 addToOpenSet(root, sid);
1622 gScore[sid] = 0;
1623 fScore[sid] = heuristic(root); // Counter
1624
1625 var steps = 0; // Main loop
1626
1627 while (openSet.size() > 0) {
1628 popFromOpenSet();
1629 steps++; // If we've found our goal, then we are done
1630
1631 if (cMinId === tid) {
1632 var path = [];
1633 var pathNode = goal;
1634 var pathNodeId = tid;
1635 var pathEdge = cameFromEdge[pathNodeId];
1636
1637 for (;;) {
1638 path.unshift(pathNode);
1639
1640 if (pathEdge != null) {
1641 path.unshift(pathEdge);
1642 }
1643
1644 pathNode = cameFrom[pathNodeId];
1645
1646 if (pathNode == null) {
1647 break;
1648 }
1649
1650 pathNodeId = pathNode.id();
1651 pathEdge = cameFromEdge[pathNodeId];
1652 }
1653
1654 return {
1655 found: true,
1656 distance: gScore[cMinId],
1657 path: this.spawn(path),
1658 steps: steps
1659 };
1660 } // Add cMin to processed nodes
1661
1662
1663 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
1664 // Take into account if graph is directed or not
1665
1666 var vwEdges = cMin._private.edges;
1667
1668 for (var i = 0; i < vwEdges.length; i++) {
1669 var e = vwEdges[i]; // edge must be in set of calling eles
1670
1671 if (!this.hasElementWithId(e.id())) {
1672 continue;
1673 } // cMin must be the source of edge if directed
1674
1675
1676 if (directed && e.data('source') !== cMinId) {
1677 continue;
1678 }
1679
1680 var wSrc = e.source();
1681 var wTgt = e.target();
1682 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
1683 var wid = w.id(); // node must be in set of calling eles
1684
1685 if (!this.hasElementWithId(wid)) {
1686 continue;
1687 } // if node is in closedSet, ignore it
1688
1689
1690 if (closedSetIds[wid]) {
1691 continue;
1692 } // New tentative score for node w
1693
1694
1695 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
1696 // w not present in openSet
1697 // OR
1698 // tentative gScore is less than previous value
1699 // w not in openSet
1700
1701 if (!isInOpenSet(wid)) {
1702 gScore[wid] = tempScore;
1703 fScore[wid] = tempScore + heuristic(w);
1704 addToOpenSet(w, wid);
1705 cameFrom[wid] = cMin;
1706 cameFromEdge[wid] = e;
1707 continue;
1708 } // w already in openSet, but with greater gScore
1709
1710
1711 if (tempScore < gScore[wid]) {
1712 gScore[wid] = tempScore;
1713 fScore[wid] = tempScore + heuristic(w);
1714 cameFrom[wid] = cMin;
1715 }
1716 } // End of neighbors update
1717
1718 } // End of main loop
1719 // If we've reached here, then we've not reached our goal
1720
1721
1722 return {
1723 found: false,
1724 distance: undefined,
1725 path: undefined,
1726 steps: steps
1727 };
1728 }
1729}; // elesfn
1730
1731var floydWarshallDefaults = defaults({
1732 weight: function weight(edge) {
1733 return 1;
1734 },
1735 directed: false
1736});
1737var elesfn$4 = {
1738 // Implemented from pseudocode from wikipedia
1739 floydWarshall: function floydWarshall(options) {
1740 var cy = this.cy();
1741
1742 var _floydWarshallDefault = floydWarshallDefaults(options),
1743 weight = _floydWarshallDefault.weight,
1744 directed = _floydWarshallDefault.directed;
1745
1746 var weightFn = weight;
1747
1748 var _this$byGroup = this.byGroup(),
1749 nodes = _this$byGroup.nodes,
1750 edges = _this$byGroup.edges;
1751
1752 var N = nodes.length;
1753 var Nsq = N * N;
1754
1755 var indexOf = function indexOf(node) {
1756 return nodes.indexOf(node);
1757 };
1758
1759 var atIndex = function atIndex(i) {
1760 return nodes[i];
1761 }; // Initialize distance matrix
1762
1763
1764 var dist = new Array(Nsq);
1765
1766 for (var n = 0; n < Nsq; n++) {
1767 var j = n % N;
1768 var i = (n - j) / N;
1769
1770 if (i === j) {
1771 dist[n] = 0;
1772 } else {
1773 dist[n] = Infinity;
1774 }
1775 } // Initialize matrix used for path reconstruction
1776 // Initialize distance matrix
1777
1778
1779 var next = new Array(Nsq);
1780 var edgeNext = new Array(Nsq); // Process edges
1781
1782 for (var _i = 0; _i < edges.length; _i++) {
1783 var edge = edges[_i];
1784 var src = edge.source()[0];
1785 var tgt = edge.target()[0];
1786
1787 if (src === tgt) {
1788 continue;
1789 } // exclude loops
1790
1791
1792 var s = indexOf(src);
1793 var t = indexOf(tgt);
1794 var st = s * N + t; // source to target index
1795
1796 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
1797
1798
1799 if (dist[st] > _weight) {
1800 dist[st] = _weight;
1801 next[st] = t;
1802 edgeNext[st] = edge;
1803 } // If undirected graph, process 'reversed' edge
1804
1805
1806 if (!directed) {
1807 var ts = t * N + s; // target to source index
1808
1809 if (!directed && dist[ts] > _weight) {
1810 dist[ts] = _weight;
1811 next[ts] = s;
1812 edgeNext[ts] = edge;
1813 }
1814 }
1815 } // Main loop
1816
1817
1818 for (var k = 0; k < N; k++) {
1819 for (var _i2 = 0; _i2 < N; _i2++) {
1820 var ik = _i2 * N + k;
1821
1822 for (var _j = 0; _j < N; _j++) {
1823 var ij = _i2 * N + _j;
1824 var kj = k * N + _j;
1825
1826 if (dist[ik] + dist[kj] < dist[ij]) {
1827 dist[ij] = dist[ik] + dist[kj];
1828 next[ij] = next[ik];
1829 }
1830 }
1831 }
1832 }
1833
1834 var getArgEle = function getArgEle(ele) {
1835 return (string(ele) ? cy.filter(ele) : ele)[0];
1836 };
1837
1838 var indexOfArgEle = function indexOfArgEle(ele) {
1839 return indexOf(getArgEle(ele));
1840 };
1841
1842 var res = {
1843 distance: function distance(from, to) {
1844 var i = indexOfArgEle(from);
1845 var j = indexOfArgEle(to);
1846 return dist[i * N + j];
1847 },
1848 path: function path(from, to) {
1849 var i = indexOfArgEle(from);
1850 var j = indexOfArgEle(to);
1851 var fromNode = atIndex(i);
1852
1853 if (i === j) {
1854 return fromNode.collection();
1855 }
1856
1857 if (next[i * N + j] == null) {
1858 return cy.collection();
1859 }
1860
1861 var path = cy.collection();
1862 var prev = i;
1863 var edge;
1864 path.merge(fromNode);
1865
1866 while (i !== j) {
1867 prev = i;
1868 i = next[i * N + j];
1869 edge = edgeNext[prev * N + i];
1870 path.merge(edge);
1871 path.merge(atIndex(i));
1872 }
1873
1874 return path;
1875 }
1876 };
1877 return res;
1878 } // floydWarshall
1879
1880}; // elesfn
1881
1882var bellmanFordDefaults = defaults({
1883 weight: function weight(edge) {
1884 return 1;
1885 },
1886 directed: false,
1887 root: null
1888});
1889var elesfn$5 = {
1890 // Implemented from pseudocode from wikipedia
1891 bellmanFord: function bellmanFord(options) {
1892 var _this = this;
1893
1894 var _bellmanFordDefaults = bellmanFordDefaults(options),
1895 weight = _bellmanFordDefaults.weight,
1896 directed = _bellmanFordDefaults.directed,
1897 root = _bellmanFordDefaults.root;
1898
1899 var weightFn = weight;
1900 var eles = this;
1901 var cy = this.cy();
1902
1903 var _this$byGroup = this.byGroup(),
1904 edges = _this$byGroup.edges,
1905 nodes = _this$byGroup.nodes;
1906
1907 var numNodes = nodes.length;
1908 var infoMap = new Map$1();
1909 var hasNegativeWeightCycle = false;
1910 var negativeWeightCycles = [];
1911 root = cy.collection(root)[0]; // in case selector passed
1912
1913 edges.unmergeBy(function (edge) {
1914 return edge.isLoop();
1915 });
1916 var numEdges = edges.length;
1917
1918 var getInfo = function getInfo(node) {
1919 var obj = infoMap.get(node.id());
1920
1921 if (!obj) {
1922 obj = {};
1923 infoMap.set(node.id(), obj);
1924 }
1925
1926 return obj;
1927 };
1928
1929 var getNodeFromTo = function getNodeFromTo(to) {
1930 return (string(to) ? cy.$(to) : to)[0];
1931 };
1932
1933 var distanceTo = function distanceTo(to) {
1934 return getInfo(getNodeFromTo(to)).dist;
1935 };
1936
1937 var pathTo = function pathTo(to) {
1938 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
1939 var end = getNodeFromTo(to);
1940 var path = [];
1941 var node = end;
1942
1943 for (;;) {
1944 if (node == null) {
1945 return _this.spawn();
1946 }
1947
1948 var _getInfo = getInfo(node),
1949 edge = _getInfo.edge,
1950 pred = _getInfo.pred;
1951
1952 path.unshift(node[0]);
1953
1954 if (node.same(thisStart) && path.length > 0) {
1955 break;
1956 }
1957
1958 if (edge != null) {
1959 path.unshift(edge);
1960 }
1961
1962 node = pred;
1963 }
1964
1965 return eles.spawn(path);
1966 }; // Initializations { dist, pred, edge }
1967
1968
1969 for (var i = 0; i < numNodes; i++) {
1970 var node = nodes[i];
1971 var info = getInfo(node);
1972
1973 if (node.same(root)) {
1974 info.dist = 0;
1975 } else {
1976 info.dist = Infinity;
1977 }
1978
1979 info.pred = null;
1980 info.edge = null;
1981 } // Edges relaxation
1982
1983
1984 var replacedEdge = false;
1985
1986 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
1987 var dist = info1.dist + weight;
1988
1989 if (dist < info2.dist && !edge.same(info1.edge)) {
1990 info2.dist = dist;
1991 info2.pred = node1;
1992 info2.edge = edge;
1993 replacedEdge = true;
1994 }
1995 };
1996
1997 for (var _i = 1; _i < numNodes; _i++) {
1998 replacedEdge = false;
1999
2000 for (var e = 0; e < numEdges; e++) {
2001 var edge = edges[e];
2002 var src = edge.source();
2003 var tgt = edge.target();
2004
2005 var _weight = weightFn(edge);
2006
2007 var srcInfo = getInfo(src);
2008 var tgtInfo = getInfo(tgt);
2009 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2010
2011 if (!directed) {
2012 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2013 }
2014 }
2015
2016 if (!replacedEdge) {
2017 break;
2018 }
2019 }
2020
2021 if (replacedEdge) {
2022 // Check for negative weight cycles
2023 for (var _e = 0; _e < numEdges; _e++) {
2024 var _edge = edges[_e];
2025
2026 var _src = _edge.source();
2027
2028 var _tgt = _edge.target();
2029
2030 var _weight2 = weightFn(_edge);
2031
2032 var srcDist = getInfo(_src).dist;
2033 var tgtDist = getInfo(_tgt).dist;
2034
2035 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2036 warn('Graph contains a negative weight cycle for Bellman-Ford');
2037 hasNegativeWeightCycle = true;
2038 break;
2039 }
2040 }
2041 }
2042
2043 return {
2044 distanceTo: distanceTo,
2045 pathTo: pathTo,
2046 hasNegativeWeightCycle: hasNegativeWeightCycle,
2047 negativeWeightCycles: negativeWeightCycles
2048 };
2049 } // bellmanFord
2050
2051}; // elesfn
2052
2053var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2054// Updates the remaining edge lists
2055// Receives as a paramater the edge which causes the collapse
2056
2057var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2058 if (remainingEdges.length === 0) {
2059 error("Karger-Stein must be run on a connected (sub)graph");
2060 }
2061
2062 var edgeInfo = remainingEdges[edgeIndex];
2063 var sourceIn = edgeInfo[1];
2064 var targetIn = edgeInfo[2];
2065 var partition1 = nodeMap[sourceIn];
2066 var partition2 = nodeMap[targetIn];
2067 var newEdges = remainingEdges; // re-use array
2068 // Delete all edges between partition1 and partition2
2069
2070 for (var i = newEdges.length - 1; i >= 0; i--) {
2071 var edge = newEdges[i];
2072 var src = edge[1];
2073 var tgt = edge[2];
2074
2075 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2076 newEdges.splice(i, 1);
2077 }
2078 } // All edges pointing to partition2 should now point to partition1
2079
2080
2081 for (var _i = 0; _i < newEdges.length; _i++) {
2082 var _edge = newEdges[_i];
2083
2084 if (_edge[1] === partition2) {
2085 // Check source
2086 newEdges[_i] = _edge.slice(); // copy
2087
2088 newEdges[_i][1] = partition1;
2089 } else if (_edge[2] === partition2) {
2090 // Check target
2091 newEdges[_i] = _edge.slice(); // copy
2092
2093 newEdges[_i][2] = partition1;
2094 }
2095 } // Move all nodes from partition2 to partition1
2096
2097
2098 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2099 if (nodeMap[_i2] === partition2) {
2100 nodeMap[_i2] = partition1;
2101 }
2102 }
2103
2104 return newEdges;
2105}; // Contracts a graph until we reach a certain number of meta nodes
2106
2107
2108var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2109 while (size > sizeLimit) {
2110 // Choose an edge randomly
2111 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2112
2113 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2114 size--;
2115 }
2116
2117 return remainingEdges;
2118};
2119
2120var elesfn$6 = {
2121 // Computes the minimum cut of an undirected graph
2122 // Returns the correct answer with high probability
2123 kargerStein: function kargerStein() {
2124 var _this = this;
2125
2126 var _this$byGroup = this.byGroup(),
2127 nodes = _this$byGroup.nodes,
2128 edges = _this$byGroup.edges;
2129
2130 edges.unmergeBy(function (edge) {
2131 return edge.isLoop();
2132 });
2133 var numNodes = nodes.length;
2134 var numEdges = edges.length;
2135 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2136 var stopSize = Math.floor(numNodes / sqrt2);
2137
2138 if (numNodes < 2) {
2139 error('At least 2 nodes are required for Karger-Stein algorithm');
2140 return undefined;
2141 } // Now store edge destination as indexes
2142 // Format for each edge (edge index, source node index, target node index)
2143
2144
2145 var edgeIndexes = [];
2146
2147 for (var i = 0; i < numEdges; i++) {
2148 var e = edges[i];
2149 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2150 } // We will store the best cut found here
2151
2152
2153 var minCutSize = Infinity;
2154 var minCutEdgeIndexes = [];
2155 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2156
2157 var metaNodeMap = new Array(numNodes);
2158 var metaNodeMap2 = new Array(numNodes);
2159
2160 var copyNodesMap = function copyNodesMap(from, to) {
2161 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2162 to[_i3] = from[_i3];
2163 }
2164 }; // Main loop
2165
2166
2167 for (var iter = 0; iter <= numIter; iter++) {
2168 // Reset meta node partition
2169 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2170 metaNodeMap[_i4] = _i4;
2171 } // Contract until stop point (stopSize nodes)
2172
2173
2174 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2175 var edgesState2 = edgesState.slice(); // copy
2176 // Create a copy of the colapsed nodes state
2177
2178 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2179
2180 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2181 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2182
2183 if (res1.length <= res2.length && res1.length < minCutSize) {
2184 minCutSize = res1.length;
2185 minCutEdgeIndexes = res1;
2186 copyNodesMap(metaNodeMap, minCutNodeMap);
2187 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2188 minCutSize = res2.length;
2189 minCutEdgeIndexes = res2;
2190 copyNodesMap(metaNodeMap2, minCutNodeMap);
2191 }
2192 } // end of main loop
2193 // Construct result
2194
2195
2196 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2197 return edges[e[0]];
2198 }));
2199 var partition1 = this.spawn();
2200 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2201
2202 var witnessNodePartition = minCutNodeMap[0];
2203
2204 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2205 var partitionId = minCutNodeMap[_i5];
2206 var node = nodes[_i5];
2207
2208 if (partitionId === witnessNodePartition) {
2209 partition1.merge(node);
2210 } else {
2211 partition2.merge(node);
2212 }
2213 } // construct components corresponding to each disjoint subset of nodes
2214
2215
2216 var constructComponent = function constructComponent(subset) {
2217 var component = _this.spawn();
2218
2219 subset.forEach(function (node) {
2220 component.merge(node);
2221 node.connectedEdges().forEach(function (edge) {
2222 // ensure edge is within calling collection and edge is not in cut
2223 if (_this.contains(edge) && !cut.contains(edge)) {
2224 component.merge(edge);
2225 }
2226 });
2227 });
2228 return component;
2229 };
2230
2231 var components = [constructComponent(partition1), constructComponent(partition2)];
2232 var ret = {
2233 cut: cut,
2234 components: components,
2235 // n.b. partitions are included to be compatible with the old api spec
2236 // (could be removed in a future major version)
2237 partition1: partition1,
2238 partition2: partition2
2239 };
2240 return ret;
2241 }
2242}; // elesfn
2243
2244var copyPosition = function copyPosition(p) {
2245 return {
2246 x: p.x,
2247 y: p.y
2248 };
2249};
2250var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
2251 return {
2252 x: p.x * zoom + pan.x,
2253 y: p.y * zoom + pan.y
2254 };
2255};
2256var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
2257 return {
2258 x: (p.x - pan.x) / zoom,
2259 y: (p.y - pan.y) / zoom
2260 };
2261};
2262var array2point = function array2point(arr) {
2263 return {
2264 x: arr[0],
2265 y: arr[1]
2266 };
2267};
2268var min = function min(arr) {
2269 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2270 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2271 var min = Infinity;
2272
2273 for (var i = begin; i < end; i++) {
2274 var val = arr[i];
2275
2276 if (isFinite(val)) {
2277 min = Math.min(val, min);
2278 }
2279 }
2280
2281 return min;
2282};
2283var max = function max(arr) {
2284 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2285 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2286 var max = -Infinity;
2287
2288 for (var i = begin; i < end; i++) {
2289 var val = arr[i];
2290
2291 if (isFinite(val)) {
2292 max = Math.max(val, max);
2293 }
2294 }
2295
2296 return max;
2297};
2298var mean = function mean(arr) {
2299 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2300 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2301 var total = 0;
2302 var n = 0;
2303
2304 for (var i = begin; i < end; i++) {
2305 var val = arr[i];
2306
2307 if (isFinite(val)) {
2308 total += val;
2309 n++;
2310 }
2311 }
2312
2313 return total / n;
2314};
2315var median = function median(arr) {
2316 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2317 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2318 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
2319 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
2320 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
2321
2322 if (copy) {
2323 arr = arr.slice(begin, end);
2324 } else {
2325 if (end < arr.length) {
2326 arr.splice(end, arr.length - end);
2327 }
2328
2329 if (begin > 0) {
2330 arr.splice(0, begin);
2331 }
2332 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
2333
2334
2335 var off = 0; // offset from non-finite values
2336
2337 for (var i = arr.length - 1; i >= 0; i--) {
2338 var v = arr[i];
2339
2340 if (includeHoles) {
2341 if (!isFinite(v)) {
2342 arr[i] = -Infinity;
2343 off++;
2344 }
2345 } else {
2346 // just remove it if we don't want to consider holes
2347 arr.splice(i, 1);
2348 }
2349 }
2350
2351 if (sort) {
2352 arr.sort(function (a, b) {
2353 return a - b;
2354 }); // requires copy = true if you don't want to change the orig
2355 }
2356
2357 var len = arr.length;
2358 var mid = Math.floor(len / 2);
2359
2360 if (len % 2 !== 0) {
2361 return arr[mid + 1 + off];
2362 } else {
2363 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
2364 }
2365};
2366var deg2rad = function deg2rad(deg) {
2367 return Math.PI * deg / 180;
2368};
2369var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
2370 return Math.atan2(dispY, dispX) - Math.PI / 2;
2371};
2372var log2 = Math.log2 || function (n) {
2373 return Math.log(n) / Math.log(2);
2374};
2375var signum = function signum(x) {
2376 if (x > 0) {
2377 return 1;
2378 } else if (x < 0) {
2379 return -1;
2380 } else {
2381 return 0;
2382 }
2383};
2384var dist = function dist(p1, p2) {
2385 return Math.sqrt(sqdist(p1, p2));
2386};
2387var sqdist = function sqdist(p1, p2) {
2388 var dx = p2.x - p1.x;
2389 var dy = p2.y - p1.y;
2390 return dx * dx + dy * dy;
2391};
2392var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
2393 var length = v.length; // First, get sum of all elements
2394
2395 var total = 0;
2396
2397 for (var i = 0; i < length; i++) {
2398 total += v[i];
2399 } // Now, divide each by the sum of all elements
2400
2401
2402 for (var _i = 0; _i < length; _i++) {
2403 v[_i] = v[_i] / total;
2404 }
2405
2406 return v;
2407};
2408
2409var qbezierAt = function qbezierAt(p0, p1, p2, t) {
2410 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
2411};
2412var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
2413 return {
2414 x: qbezierAt(p0.x, p1.x, p2.x, t),
2415 y: qbezierAt(p0.y, p1.y, p2.y, t)
2416 };
2417};
2418var lineAt = function lineAt(p0, p1, t, d) {
2419 var vec = {
2420 x: p1.x - p0.x,
2421 y: p1.y - p0.y
2422 };
2423 var vecDist = dist(p0, p1);
2424 var normVec = {
2425 x: vec.x / vecDist,
2426 y: vec.y / vecDist
2427 };
2428 t = t == null ? 0 : t;
2429 d = d != null ? d : t * vecDist;
2430 return {
2431 x: p0.x + normVec.x * d,
2432 y: p0.y + normVec.y * d
2433 };
2434};
2435var bound = function bound(min, val, max) {
2436 return Math.max(min, Math.min(max, val));
2437}; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
2438
2439var makeBoundingBox = function makeBoundingBox(bb) {
2440 if (bb == null) {
2441 return {
2442 x1: Infinity,
2443 y1: Infinity,
2444 x2: -Infinity,
2445 y2: -Infinity,
2446 w: 0,
2447 h: 0
2448 };
2449 } else if (bb.x1 != null && bb.y1 != null) {
2450 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
2451 return {
2452 x1: bb.x1,
2453 y1: bb.y1,
2454 x2: bb.x2,
2455 y2: bb.y2,
2456 w: bb.x2 - bb.x1,
2457 h: bb.y2 - bb.y1
2458 };
2459 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
2460 return {
2461 x1: bb.x1,
2462 y1: bb.y1,
2463 x2: bb.x1 + bb.w,
2464 y2: bb.y1 + bb.h,
2465 w: bb.w,
2466 h: bb.h
2467 };
2468 }
2469 }
2470};
2471var copyBoundingBox = function copyBoundingBox(bb) {
2472 return {
2473 x1: bb.x1,
2474 x2: bb.x2,
2475 w: bb.w,
2476 y1: bb.y1,
2477 y2: bb.y2,
2478 h: bb.h
2479 };
2480};
2481var clearBoundingBox = function clearBoundingBox(bb) {
2482 bb.x1 = Infinity;
2483 bb.y1 = Infinity;
2484 bb.x2 = -Infinity;
2485 bb.y2 = -Infinity;
2486 bb.w = 0;
2487 bb.h = 0;
2488};
2489var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
2490 // update bb1 with bb2 bounds
2491 bb1.x1 = Math.min(bb1.x1, bb2.x1);
2492 bb1.x2 = Math.max(bb1.x2, bb2.x2);
2493 bb1.w = bb1.x2 - bb1.x1;
2494 bb1.y1 = Math.min(bb1.y1, bb2.y1);
2495 bb1.y2 = Math.max(bb1.y2, bb2.y2);
2496 bb1.h = bb1.y2 - bb1.y1;
2497};
2498var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
2499 bb.x1 = Math.min(bb.x1, x);
2500 bb.x2 = Math.max(bb.x2, x);
2501 bb.w = bb.x2 - bb.x1;
2502 bb.y1 = Math.min(bb.y1, y);
2503 bb.y2 = Math.max(bb.y2, y);
2504 bb.h = bb.y2 - bb.y1;
2505};
2506var expandBoundingBox = function expandBoundingBox(bb) {
2507 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2508 bb.x1 -= padding;
2509 bb.x2 += padding;
2510 bb.y1 -= padding;
2511 bb.y2 += padding;
2512 bb.w = bb.x2 - bb.x1;
2513 bb.h = bb.y2 - bb.y1;
2514 return bb;
2515};
2516var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
2517 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
2518 var top, right, bottom, left;
2519
2520 if (padding.length === 1) {
2521 top = right = bottom = left = padding[0];
2522 } else if (padding.length === 2) {
2523 top = bottom = padding[0];
2524 left = right = padding[1];
2525 } else if (padding.length === 4) {
2526 var _padding = _slicedToArray(padding, 4);
2527
2528 top = _padding[0];
2529 right = _padding[1];
2530 bottom = _padding[2];
2531 left = _padding[3];
2532 }
2533
2534 bb.x1 -= left;
2535 bb.x2 += right;
2536 bb.y1 -= top;
2537 bb.y2 += bottom;
2538 bb.w = bb.x2 - bb.x1;
2539 bb.h = bb.y2 - bb.y1;
2540 return bb;
2541};
2542
2543var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
2544 bb1.x1 = bb2.x1;
2545 bb1.y1 = bb2.y1;
2546 bb1.x2 = bb2.x2;
2547 bb1.y2 = bb2.y2;
2548 bb1.w = bb1.x2 - bb1.x1;
2549 bb1.h = bb1.y2 - bb1.y1;
2550};
2551var assignShiftToBoundingBox = function assignShiftToBoundingBox(bb, delta) {
2552 bb.x1 += delta.x;
2553 bb.x2 += delta.x;
2554 bb.y1 += delta.y;
2555 bb.y2 += delta.y;
2556};
2557var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
2558 // case: one bb to right of other
2559 if (bb1.x1 > bb2.x2) {
2560 return false;
2561 }
2562
2563 if (bb2.x1 > bb1.x2) {
2564 return false;
2565 } // case: one bb to left of other
2566
2567
2568 if (bb1.x2 < bb2.x1) {
2569 return false;
2570 }
2571
2572 if (bb2.x2 < bb1.x1) {
2573 return false;
2574 } // case: one bb above other
2575
2576
2577 if (bb1.y2 < bb2.y1) {
2578 return false;
2579 }
2580
2581 if (bb2.y2 < bb1.y1) {
2582 return false;
2583 } // case: one bb below other
2584
2585
2586 if (bb1.y1 > bb2.y2) {
2587 return false;
2588 }
2589
2590 if (bb2.y1 > bb1.y2) {
2591 return false;
2592 } // otherwise, must have some overlap
2593
2594
2595 return true;
2596};
2597var inBoundingBox = function inBoundingBox(bb, x, y) {
2598 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
2599};
2600var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
2601 return inBoundingBox(bb, pt.x, pt.y);
2602};
2603var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
2604 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
2605};
2606var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
2607 var cornerRadius = getRoundRectangleRadius(width, height);
2608 var halfWidth = width / 2;
2609 var halfHeight = height / 2; // Check intersections with straight line segments
2610
2611 var straightLineIntersections; // Top segment, left to right
2612
2613 {
2614 var topStartX = nodeX - halfWidth + cornerRadius - padding;
2615 var topStartY = nodeY - halfHeight - padding;
2616 var topEndX = nodeX + halfWidth - cornerRadius + padding;
2617 var topEndY = topStartY;
2618 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
2619
2620 if (straightLineIntersections.length > 0) {
2621 return straightLineIntersections;
2622 }
2623 } // Right segment, top to bottom
2624
2625 {
2626 var rightStartX = nodeX + halfWidth + padding;
2627 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
2628 var rightEndX = rightStartX;
2629 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
2630 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
2631
2632 if (straightLineIntersections.length > 0) {
2633 return straightLineIntersections;
2634 }
2635 } // Bottom segment, left to right
2636
2637 {
2638 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
2639 var bottomStartY = nodeY + halfHeight + padding;
2640 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
2641 var bottomEndY = bottomStartY;
2642 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
2643
2644 if (straightLineIntersections.length > 0) {
2645 return straightLineIntersections;
2646 }
2647 } // Left segment, top to bottom
2648
2649 {
2650 var leftStartX = nodeX - halfWidth - padding;
2651 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
2652 var leftEndX = leftStartX;
2653 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
2654 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
2655
2656 if (straightLineIntersections.length > 0) {
2657 return straightLineIntersections;
2658 }
2659 } // Check intersections with arc segments
2660
2661 var arcIntersections; // Top Left
2662
2663 {
2664 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
2665 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
2666 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2667
2668 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
2669 return [arcIntersections[0], arcIntersections[1]];
2670 }
2671 } // Top Right
2672
2673 {
2674 var topRightCenterX = nodeX + halfWidth - cornerRadius;
2675 var topRightCenterY = nodeY - halfHeight + cornerRadius;
2676 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2677
2678 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
2679 return [arcIntersections[0], arcIntersections[1]];
2680 }
2681 } // Bottom Right
2682
2683 {
2684 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
2685 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
2686 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2687
2688 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
2689 return [arcIntersections[0], arcIntersections[1]];
2690 }
2691 } // Bottom Left
2692
2693 {
2694 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
2695 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
2696 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2697
2698 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
2699 return [arcIntersections[0], arcIntersections[1]];
2700 }
2701 }
2702 return []; // if nothing
2703};
2704var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
2705 var t = tolerance;
2706 var x1 = Math.min(lx1, lx2);
2707 var x2 = Math.max(lx1, lx2);
2708 var y1 = Math.min(ly1, ly2);
2709 var y2 = Math.max(ly1, ly2);
2710 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
2711};
2712var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
2713 var bb = {
2714 x1: Math.min(x1, x3, x2) - tolerance,
2715 x2: Math.max(x1, x3, x2) + tolerance,
2716 y1: Math.min(y1, y3, y2) - tolerance,
2717 y2: Math.max(y1, y3, y2) + tolerance
2718 }; // if outside the rough bounding box for the bezier, then it can't be a hit
2719
2720 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
2721 // console.log('bezier out of rough bb')
2722 return false;
2723 } else {
2724 // console.log('do more expensive check');
2725 return true;
2726 }
2727};
2728var solveQuadratic = function solveQuadratic(a, b, c, val) {
2729 c -= val;
2730 var r = b * b - 4 * a * c;
2731
2732 if (r < 0) {
2733 return [];
2734 }
2735
2736 var sqrtR = Math.sqrt(r);
2737 var denom = 2 * a;
2738 var root1 = (-b + sqrtR) / denom;
2739 var root2 = (-b - sqrtR) / denom;
2740 return [root1, root2];
2741};
2742var solveCubic = function solveCubic(a, b, c, d, result) {
2743 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
2744 // r is the real component, i is the imaginary component
2745 // An implementation of the Cardano method from the year 1545
2746 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
2747 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
2748
2749 if (a === 0) {
2750 a = epsilon;
2751 }
2752
2753 b /= a;
2754 c /= a;
2755 d /= a;
2756 var discriminant, q, r, dum1, s, t, term1, r13;
2757 q = (3.0 * c - b * b) / 9.0;
2758 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
2759 r /= 54.0;
2760 discriminant = q * q * q + r * r;
2761 result[1] = 0;
2762 term1 = b / 3.0;
2763
2764 if (discriminant > 0) {
2765 s = r + Math.sqrt(discriminant);
2766 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
2767 t = r - Math.sqrt(discriminant);
2768 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
2769 result[0] = -term1 + s + t;
2770 term1 += (s + t) / 2.0;
2771 result[4] = result[2] = -term1;
2772 term1 = Math.sqrt(3.0) * (-t + s) / 2;
2773 result[3] = term1;
2774 result[5] = -term1;
2775 return;
2776 }
2777
2778 result[5] = result[3] = 0;
2779
2780 if (discriminant === 0) {
2781 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
2782 result[0] = -term1 + 2.0 * r13;
2783 result[4] = result[2] = -(r13 + term1);
2784 return;
2785 }
2786
2787 q = -q;
2788 dum1 = q * q * q;
2789 dum1 = Math.acos(r / Math.sqrt(dum1));
2790 r13 = 2.0 * Math.sqrt(q);
2791 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
2792 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
2793 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
2794 return;
2795};
2796var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
2797 // Find minimum distance by using the minimum of the distance
2798 // function between the given point and the curve
2799 // This gives the coefficients of the resulting cubic equation
2800 // whose roots tell us where a possible minimum is
2801 // (Coefficients are divided by 4)
2802 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;
2803 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;
2804 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;
2805 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);
2806
2807 var roots = []; // Use the cubic solving algorithm
2808
2809 solveCubic(a, b, c, d, roots);
2810 var zeroThreshold = 0.0000001;
2811 var params = [];
2812
2813 for (var index = 0; index < 6; index += 2) {
2814 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
2815 params.push(roots[index]);
2816 }
2817 }
2818
2819 params.push(1.0);
2820 params.push(0.0);
2821 var minDistanceSquared = -1;
2822 var curX, curY, distSquared;
2823
2824 for (var i = 0; i < params.length; i++) {
2825 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
2826 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
2827 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
2828
2829 if (minDistanceSquared >= 0) {
2830 if (distSquared < minDistanceSquared) {
2831 minDistanceSquared = distSquared;
2832 }
2833 } else {
2834 minDistanceSquared = distSquared;
2835 }
2836 }
2837
2838 return minDistanceSquared;
2839};
2840var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
2841 var offset = [x - x1, y - y1];
2842 var line = [x2 - x1, y2 - y1];
2843 var lineSq = line[0] * line[0] + line[1] * line[1];
2844 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
2845 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
2846 var adjSq = dotProduct * dotProduct / lineSq;
2847
2848 if (dotProduct < 0) {
2849 return hypSq;
2850 }
2851
2852 if (adjSq > lineSq) {
2853 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
2854 }
2855
2856 return hypSq - adjSq;
2857};
2858var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
2859 var x1, y1, x2, y2;
2860 var y3; // Intersect with vertical line through (x, y)
2861
2862 var up = 0; // let down = 0;
2863
2864 for (var i = 0; i < points.length / 2; i++) {
2865 x1 = points[i * 2];
2866 y1 = points[i * 2 + 1];
2867
2868 if (i + 1 < points.length / 2) {
2869 x2 = points[(i + 1) * 2];
2870 y2 = points[(i + 1) * 2 + 1];
2871 } else {
2872 x2 = points[(i + 1 - points.length / 2) * 2];
2873 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
2874 }
2875
2876 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
2877 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
2878
2879 if (y3 > y) {
2880 up++;
2881 } // if( y3 < y ){
2882 // down++;
2883 // }
2884
2885 } else {
2886 continue;
2887 }
2888 }
2889
2890 if (up % 2 === 0) {
2891 return false;
2892 } else {
2893 return true;
2894 }
2895};
2896var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
2897 var transformedPoints = new Array(basePoints.length); // Gives negative angle
2898
2899 var angle;
2900
2901 if (direction[0] != null) {
2902 angle = Math.atan(direction[1] / direction[0]);
2903
2904 if (direction[0] < 0) {
2905 angle = angle + Math.PI / 2;
2906 } else {
2907 angle = -angle - Math.PI / 2;
2908 }
2909 } else {
2910 angle = direction;
2911 }
2912
2913 var cos = Math.cos(-angle);
2914 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
2915
2916 for (var i = 0; i < transformedPoints.length / 2; i++) {
2917 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
2918 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
2919 transformedPoints[i * 2] += centerX;
2920 transformedPoints[i * 2 + 1] += centerY;
2921 }
2922
2923 var points;
2924
2925 if (padding > 0) {
2926 var expandedLineSet = expandPolygon(transformedPoints, -padding);
2927 points = joinLines(expandedLineSet);
2928 } else {
2929 points = transformedPoints;
2930 }
2931
2932 return pointInsidePolygonPoints(x, y, points);
2933};
2934var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
2935 var cutPolygonPoints = new Array(basePoints.length);
2936 var halfW = width / 2;
2937 var halfH = height / 2;
2938 var cornerRadius = getRoundPolygonRadius(width, height);
2939 var squaredCornerRadius = cornerRadius * cornerRadius;
2940
2941 for (var i = 0; i < basePoints.length / 4; i++) {
2942 var sourceUv = void 0,
2943 destUv = void 0;
2944
2945 if (i === 0) {
2946 sourceUv = basePoints.length - 2;
2947 } else {
2948 sourceUv = i * 4 - 2;
2949 }
2950
2951 destUv = i * 4 + 2;
2952 var px = centerX + halfW * basePoints[i * 4];
2953 var py = centerY + halfH * basePoints[i * 4 + 1];
2954 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
2955 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
2956 var cp0x = px - offset * basePoints[sourceUv];
2957 var cp0y = py - offset * basePoints[sourceUv + 1];
2958 var cp1x = px + offset * basePoints[destUv];
2959 var cp1y = py + offset * basePoints[destUv + 1];
2960 cutPolygonPoints[i * 4] = cp0x;
2961 cutPolygonPoints[i * 4 + 1] = cp0y;
2962 cutPolygonPoints[i * 4 + 2] = cp1x;
2963 cutPolygonPoints[i * 4 + 3] = cp1y;
2964 var orthx = basePoints[sourceUv + 1];
2965 var orthy = -basePoints[sourceUv];
2966 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
2967
2968 if (cosAlpha < 0) {
2969 orthx *= -1;
2970 orthy *= -1;
2971 }
2972
2973 var cx = cp0x + orthx * cornerRadius;
2974 var cy = cp0y + orthy * cornerRadius;
2975 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
2976
2977 if (squaredDistance <= squaredCornerRadius) {
2978 return true;
2979 }
2980 }
2981
2982 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
2983};
2984var joinLines = function joinLines(lineSet) {
2985 var vertices = new Array(lineSet.length / 2);
2986 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
2987 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
2988
2989 for (var i = 0; i < lineSet.length / 4; i++) {
2990 currentLineStartX = lineSet[i * 4];
2991 currentLineStartY = lineSet[i * 4 + 1];
2992 currentLineEndX = lineSet[i * 4 + 2];
2993 currentLineEndY = lineSet[i * 4 + 3];
2994
2995 if (i < lineSet.length / 4 - 1) {
2996 nextLineStartX = lineSet[(i + 1) * 4];
2997 nextLineStartY = lineSet[(i + 1) * 4 + 1];
2998 nextLineEndX = lineSet[(i + 1) * 4 + 2];
2999 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3000 } else {
3001 nextLineStartX = lineSet[0];
3002 nextLineStartY = lineSet[1];
3003 nextLineEndX = lineSet[2];
3004 nextLineEndY = lineSet[3];
3005 }
3006
3007 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3008 vertices[i * 2] = intersection[0];
3009 vertices[i * 2 + 1] = intersection[1];
3010 }
3011
3012 return vertices;
3013};
3014var expandPolygon = function expandPolygon(points, pad) {
3015 var expandedLineSet = new Array(points.length * 2);
3016 var currentPointX, currentPointY, nextPointX, nextPointY;
3017
3018 for (var i = 0; i < points.length / 2; i++) {
3019 currentPointX = points[i * 2];
3020 currentPointY = points[i * 2 + 1];
3021
3022 if (i < points.length / 2 - 1) {
3023 nextPointX = points[(i + 1) * 2];
3024 nextPointY = points[(i + 1) * 2 + 1];
3025 } else {
3026 nextPointX = points[0];
3027 nextPointY = points[1];
3028 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3029 // Assume CCW polygon winding
3030
3031
3032 var offsetX = nextPointY - currentPointY;
3033 var offsetY = -(nextPointX - currentPointX); // Normalize
3034
3035 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3036 var normalizedOffsetX = offsetX / offsetLength;
3037 var normalizedOffsetY = offsetY / offsetLength;
3038 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3039 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3040 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3041 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3042 }
3043
3044 return expandedLineSet;
3045};
3046var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3047 var dispX = centerX - x;
3048 var dispY = centerY - y;
3049 dispX /= ellipseWradius;
3050 dispY /= ellipseHradius;
3051 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3052 var newLength = len - 1;
3053
3054 if (newLength < 0) {
3055 return [];
3056 }
3057
3058 var lenProportion = newLength / len;
3059 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3060};
3061var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3062 x -= centerX;
3063 y -= centerY;
3064 x /= width / 2 + padding;
3065 y /= height / 2 + padding;
3066 return x * x + y * y <= 1;
3067}; // Returns intersections of increasing distance from line's start point
3068
3069var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3070 // Calculate d, direction vector of line
3071 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3072
3073 var f = [x1 - centerX, y1 - centerY];
3074 var a = d[0] * d[0] + d[1] * d[1];
3075 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3076 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3077 var discriminant = b * b - 4 * a * c;
3078
3079 if (discriminant < 0) {
3080 return [];
3081 }
3082
3083 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3084 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3085 var tMin = Math.min(t1, t2);
3086 var tMax = Math.max(t1, t2);
3087 var inRangeParams = [];
3088
3089 if (tMin >= 0 && tMin <= 1) {
3090 inRangeParams.push(tMin);
3091 }
3092
3093 if (tMax >= 0 && tMax <= 1) {
3094 inRangeParams.push(tMax);
3095 }
3096
3097 if (inRangeParams.length === 0) {
3098 return [];
3099 }
3100
3101 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3102 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3103
3104 if (inRangeParams.length > 1) {
3105 if (inRangeParams[0] == inRangeParams[1]) {
3106 return [nearIntersectionX, nearIntersectionY];
3107 } else {
3108 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3109 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3110 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3111 }
3112 } else {
3113 return [nearIntersectionX, nearIntersectionY];
3114 }
3115};
3116var midOfThree = function midOfThree(a, b, c) {
3117 if (b <= a && a <= c || c <= a && a <= b) {
3118 return a;
3119 } else if (a <= b && b <= c || c <= b && b <= a) {
3120 return b;
3121 } else {
3122 return c;
3123 }
3124}; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3125
3126var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3127 var dx13 = x1 - x3;
3128 var dx21 = x2 - x1;
3129 var dx43 = x4 - x3;
3130 var dy13 = y1 - y3;
3131 var dy21 = y2 - y1;
3132 var dy43 = y4 - y3;
3133 var ua_t = dx43 * dy13 - dy43 * dx13;
3134 var ub_t = dx21 * dy13 - dy21 * dx13;
3135 var u_b = dy43 * dx21 - dx43 * dy21;
3136
3137 if (u_b !== 0) {
3138 var ua = ua_t / u_b;
3139 var ub = ub_t / u_b;
3140 var flptThreshold = 0.001;
3141
3142 var _min = 0 - flptThreshold;
3143
3144 var _max = 1 + flptThreshold;
3145
3146 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3147 return [x1 + ua * dx21, y1 + ua * dy21];
3148 } else {
3149 if (!infiniteLines) {
3150 return [];
3151 } else {
3152 return [x1 + ua * dx21, y1 + ua * dy21];
3153 }
3154 }
3155 } else {
3156 if (ua_t === 0 || ub_t === 0) {
3157 // Parallel, coincident lines. Check if overlap
3158 // Check endpoint of second line
3159 if (midOfThree(x1, x2, x4) === x4) {
3160 return [x4, y4];
3161 } // Check start point of second line
3162
3163
3164 if (midOfThree(x1, x2, x3) === x3) {
3165 return [x3, y3];
3166 } // Endpoint of first line
3167
3168
3169 if (midOfThree(x3, x4, x2) === x2) {
3170 return [x2, y2];
3171 }
3172
3173 return [];
3174 } else {
3175 // Parallel, non-coincident
3176 return [];
3177 }
3178 }
3179}; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3180// intersect a node polygon (pts transformed)
3181//
3182// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3183// intersect the points (no transform)
3184
3185var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3186 var intersections = [];
3187 var intersection;
3188 var transformedPoints = new Array(basePoints.length);
3189 var doTransform = true;
3190
3191 if (width == null) {
3192 doTransform = false;
3193 }
3194
3195 var points;
3196
3197 if (doTransform) {
3198 for (var i = 0; i < transformedPoints.length / 2; i++) {
3199 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3200 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3201 }
3202
3203 if (padding > 0) {
3204 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3205 points = joinLines(expandedLineSet);
3206 } else {
3207 points = transformedPoints;
3208 }
3209 } else {
3210 points = basePoints;
3211 }
3212
3213 var currentX, currentY, nextX, nextY;
3214
3215 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3216 currentX = points[_i2 * 2];
3217 currentY = points[_i2 * 2 + 1];
3218
3219 if (_i2 < points.length / 2 - 1) {
3220 nextX = points[(_i2 + 1) * 2];
3221 nextY = points[(_i2 + 1) * 2 + 1];
3222 } else {
3223 nextX = points[0];
3224 nextY = points[1];
3225 }
3226
3227 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3228
3229 if (intersection.length !== 0) {
3230 intersections.push(intersection[0], intersection[1]);
3231 }
3232 }
3233
3234 return intersections;
3235};
3236var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3237 var intersections = [];
3238 var intersection;
3239 var lines = new Array(basePoints.length);
3240 var halfW = width / 2;
3241 var halfH = height / 2;
3242 var cornerRadius = getRoundPolygonRadius(width, height);
3243
3244 for (var i = 0; i < basePoints.length / 4; i++) {
3245 var sourceUv = void 0,
3246 destUv = void 0;
3247
3248 if (i === 0) {
3249 sourceUv = basePoints.length - 2;
3250 } else {
3251 sourceUv = i * 4 - 2;
3252 }
3253
3254 destUv = i * 4 + 2;
3255 var px = centerX + halfW * basePoints[i * 4];
3256 var py = centerY + halfH * basePoints[i * 4 + 1];
3257 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3258 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3259 var cp0x = px - offset * basePoints[sourceUv];
3260 var cp0y = py - offset * basePoints[sourceUv + 1];
3261 var cp1x = px + offset * basePoints[destUv];
3262 var cp1y = py + offset * basePoints[destUv + 1];
3263
3264 if (i === 0) {
3265 lines[basePoints.length - 2] = cp0x;
3266 lines[basePoints.length - 1] = cp0y;
3267 } else {
3268 lines[i * 4 - 2] = cp0x;
3269 lines[i * 4 - 1] = cp0y;
3270 }
3271
3272 lines[i * 4] = cp1x;
3273 lines[i * 4 + 1] = cp1y;
3274 var orthx = basePoints[sourceUv + 1];
3275 var orthy = -basePoints[sourceUv];
3276 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3277
3278 if (cosAlpha < 0) {
3279 orthx *= -1;
3280 orthy *= -1;
3281 }
3282
3283 var cx = cp0x + orthx * cornerRadius;
3284 var cy = cp0y + orthy * cornerRadius;
3285 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
3286
3287 if (intersection.length !== 0) {
3288 intersections.push(intersection[0], intersection[1]);
3289 }
3290 }
3291
3292 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
3293 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
3294
3295 if (intersection.length !== 0) {
3296 intersections.push(intersection[0], intersection[1]);
3297 }
3298 }
3299
3300 if (intersections.length > 2) {
3301 var lowestIntersection = [intersections[0], intersections[1]];
3302 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3303
3304 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
3305 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
3306
3307 if (squaredDistance <= lowestSquaredDistance) {
3308 lowestIntersection[0] = intersections[_i4 * 2];
3309 lowestIntersection[1] = intersections[_i4 * 2 + 1];
3310 lowestSquaredDistance = squaredDistance;
3311 }
3312 }
3313
3314 return lowestIntersection;
3315 }
3316
3317 return intersections;
3318};
3319var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3320 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3321 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3322 var lenRatio = (length - amount) / length;
3323
3324 if (lenRatio < 0) {
3325 lenRatio = 0.00001;
3326 }
3327
3328 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3329};
3330var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3331 var points = generateUnitNgonPoints(sides, rotationRadians);
3332 points = fitPolygonToSquare(points);
3333 return points;
3334};
3335var fitPolygonToSquare = function fitPolygonToSquare(points) {
3336 var x, y;
3337 var sides = points.length / 2;
3338 var minX = Infinity,
3339 minY = Infinity,
3340 maxX = -Infinity,
3341 maxY = -Infinity;
3342
3343 for (var i = 0; i < sides; i++) {
3344 x = points[2 * i];
3345 y = points[2 * i + 1];
3346 minX = Math.min(minX, x);
3347 maxX = Math.max(maxX, x);
3348 minY = Math.min(minY, y);
3349 maxY = Math.max(maxY, y);
3350 } // stretch factors
3351
3352
3353 var sx = 2 / (maxX - minX);
3354 var sy = 2 / (maxY - minY);
3355
3356 for (var _i5 = 0; _i5 < sides; _i5++) {
3357 x = points[2 * _i5] = points[2 * _i5] * sx;
3358 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
3359 minX = Math.min(minX, x);
3360 maxX = Math.max(maxX, x);
3361 minY = Math.min(minY, y);
3362 maxY = Math.max(maxY, y);
3363 }
3364
3365 if (minY < -1) {
3366 for (var _i6 = 0; _i6 < sides; _i6++) {
3367 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
3368 }
3369 }
3370
3371 return points;
3372};
3373var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
3374 var increment = 1.0 / sides * 2 * Math.PI;
3375 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
3376 startAngle += rotationRadians;
3377 var points = new Array(sides * 2);
3378 var currentAngle;
3379
3380 for (var i = 0; i < sides; i++) {
3381 currentAngle = i * increment + startAngle;
3382 points[2 * i] = Math.cos(currentAngle); // x
3383
3384 points[2 * i + 1] = Math.sin(-currentAngle); // y
3385 }
3386
3387 return points;
3388}; // Set the default radius, unless half of width or height is smaller than default
3389
3390var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
3391 return Math.min(width / 4, height / 4, 8);
3392}; // Set the default radius
3393
3394var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
3395 return Math.min(width / 10, height / 10, 8);
3396};
3397var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
3398 return 8;
3399};
3400var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
3401 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
3402}; // get curve width, height, and control point position offsets as a percentage of node height / width
3403
3404var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
3405 return {
3406 heightOffset: Math.min(15, 0.05 * height),
3407 widthOffset: Math.min(100, 0.25 * width),
3408 ctrlPtOffsetPct: 0.05
3409 };
3410};
3411
3412var pageRankDefaults = defaults({
3413 dampingFactor: 0.8,
3414 precision: 0.000001,
3415 iterations: 200,
3416 weight: function weight(edge) {
3417 return 1;
3418 }
3419});
3420var elesfn$7 = {
3421 pageRank: function pageRank(options) {
3422 var _pageRankDefaults = pageRankDefaults(options),
3423 dampingFactor = _pageRankDefaults.dampingFactor,
3424 precision = _pageRankDefaults.precision,
3425 iterations = _pageRankDefaults.iterations,
3426 weight = _pageRankDefaults.weight;
3427
3428 var cy = this._private.cy;
3429
3430 var _this$byGroup = this.byGroup(),
3431 nodes = _this$byGroup.nodes,
3432 edges = _this$byGroup.edges;
3433
3434 var numNodes = nodes.length;
3435 var numNodesSqd = numNodes * numNodes;
3436 var numEdges = edges.length; // Construct transposed adjacency matrix
3437 // First lets have a zeroed matrix of the right size
3438 // We'll also keep track of the sum of each column
3439
3440 var matrix = new Array(numNodesSqd);
3441 var columnSum = new Array(numNodes);
3442 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
3443
3444 for (var i = 0; i < numNodes; i++) {
3445 for (var j = 0; j < numNodes; j++) {
3446 var n = i * numNodes + j;
3447 matrix[n] = 0;
3448 }
3449
3450 columnSum[i] = 0;
3451 } // Now, process edges
3452
3453
3454 for (var _i = 0; _i < numEdges; _i++) {
3455 var edge = edges[_i];
3456 var srcId = edge.data('source');
3457 var tgtId = edge.data('target'); // Don't include loops in the matrix
3458
3459 if (srcId === tgtId) {
3460 continue;
3461 }
3462
3463 var s = nodes.indexOfId(srcId);
3464 var t = nodes.indexOfId(tgtId);
3465 var w = weight(edge);
3466
3467 var _n = t * numNodes + s; // Update matrix
3468
3469
3470 matrix[_n] += w; // Update column sum
3471
3472 columnSum[s] += w;
3473 } // Add additional probability based on damping factor
3474 // Also, take into account columns that have sum = 0
3475
3476
3477 var p = 1.0 / numNodes + additionalProb; // Shorthand
3478 // Traverse matrix, column by column
3479
3480 for (var _j = 0; _j < numNodes; _j++) {
3481 if (columnSum[_j] === 0) {
3482 // No 'links' out from node jth, assume equal probability for each possible node
3483 for (var _i2 = 0; _i2 < numNodes; _i2++) {
3484 var _n2 = _i2 * numNodes + _j;
3485
3486 matrix[_n2] = p;
3487 }
3488 } else {
3489 // Node jth has outgoing link, compute normalized probabilities
3490 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3491 var _n3 = _i3 * numNodes + _j;
3492
3493 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
3494 }
3495 }
3496 } // Compute dominant eigenvector using power method
3497
3498
3499 var eigenvector = new Array(numNodes);
3500 var temp = new Array(numNodes);
3501 var previous; // Start with a vector of all 1's
3502 // Also, initialize a null vector which will be used as shorthand
3503
3504 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3505 eigenvector[_i4] = 1;
3506 }
3507
3508 for (var iter = 0; iter < iterations; iter++) {
3509 // Temp array with all 0's
3510 for (var _i5 = 0; _i5 < numNodes; _i5++) {
3511 temp[_i5] = 0;
3512 } // Multiply matrix with previous result
3513
3514
3515 for (var _i6 = 0; _i6 < numNodes; _i6++) {
3516 for (var _j2 = 0; _j2 < numNodes; _j2++) {
3517 var _n4 = _i6 * numNodes + _j2;
3518
3519 temp[_i6] += matrix[_n4] * eigenvector[_j2];
3520 }
3521 }
3522
3523 inPlaceSumNormalize(temp);
3524 previous = eigenvector;
3525 eigenvector = temp;
3526 temp = previous;
3527 var diff = 0; // Compute difference (squared module) of both vectors
3528
3529 for (var _i7 = 0; _i7 < numNodes; _i7++) {
3530 var delta = previous[_i7] - eigenvector[_i7];
3531 diff += delta * delta;
3532 } // If difference is less than the desired threshold, stop iterating
3533
3534
3535 if (diff < precision) {
3536 break;
3537 }
3538 } // Construct result
3539
3540
3541 var res = {
3542 rank: function rank(node) {
3543 node = cy.collection(node)[0];
3544 return eigenvector[nodes.indexOf(node)];
3545 }
3546 };
3547 return res;
3548 } // pageRank
3549
3550}; // elesfn
3551
3552var defaults$1 = defaults({
3553 root: null,
3554 weight: function weight(edge) {
3555 return 1;
3556 },
3557 directed: false,
3558 alpha: 0
3559});
3560var elesfn$8 = {
3561 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
3562 options = defaults$1(options);
3563 var cy = this.cy();
3564 var nodes = this.nodes();
3565 var numNodes = nodes.length;
3566
3567 if (!options.directed) {
3568 var degrees = {};
3569 var maxDegree = 0;
3570
3571 for (var i = 0; i < numNodes; i++) {
3572 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
3573
3574 options.root = node;
3575 var currDegree = this.degreeCentrality(options);
3576
3577 if (maxDegree < currDegree.degree) {
3578 maxDegree = currDegree.degree;
3579 }
3580
3581 degrees[node.id()] = currDegree.degree;
3582 }
3583
3584 return {
3585 degree: function degree(node) {
3586 if (maxDegree === 0) {
3587 return 0;
3588 }
3589
3590 if (string(node)) {
3591 // from is a selector string
3592 node = cy.filter(node);
3593 }
3594
3595 return degrees[node.id()] / maxDegree;
3596 }
3597 };
3598 } else {
3599 var indegrees = {};
3600 var outdegrees = {};
3601 var maxIndegree = 0;
3602 var maxOutdegree = 0;
3603
3604 for (var _i = 0; _i < numNodes; _i++) {
3605 var _node = nodes[_i];
3606
3607 var id = _node.id(); // add current node to the current options object and call degreeCentrality
3608
3609
3610 options.root = _node;
3611
3612 var _currDegree = this.degreeCentrality(options);
3613
3614 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
3615 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
3616 indegrees[id] = _currDegree.indegree;
3617 outdegrees[id] = _currDegree.outdegree;
3618 }
3619
3620 return {
3621 indegree: function indegree(node) {
3622 if (maxIndegree == 0) {
3623 return 0;
3624 }
3625
3626 if (string(node)) {
3627 // from is a selector string
3628 node = cy.filter(node);
3629 }
3630
3631 return indegrees[node.id()] / maxIndegree;
3632 },
3633 outdegree: function outdegree(node) {
3634 if (maxOutdegree === 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 outdegrees[node.id()] / maxOutdegree;
3644 }
3645 };
3646 }
3647 },
3648 // degreeCentralityNormalized
3649 // Implemented from the algorithm in Opsahl's paper
3650 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
3651 // check the heading 2 "Degree"
3652 degreeCentrality: function degreeCentrality(options) {
3653 options = defaults$1(options);
3654 var cy = this.cy();
3655 var callingEles = this;
3656 var _options = options,
3657 root = _options.root,
3658 weight = _options.weight,
3659 directed = _options.directed,
3660 alpha = _options.alpha;
3661 root = cy.collection(root)[0];
3662
3663 if (!directed) {
3664 var connEdges = root.connectedEdges().intersection(callingEles);
3665 var k = connEdges.length;
3666 var s = 0; // Now, sum edge weights
3667
3668 for (var i = 0; i < connEdges.length; i++) {
3669 s += weight(connEdges[i]);
3670 }
3671
3672 return {
3673 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
3674 };
3675 } else {
3676 var edges = root.connectedEdges();
3677 var incoming = edges.filter(function (edge) {
3678 return edge.target().same(root) && callingEles.has(edge);
3679 });
3680 var outgoing = edges.filter(function (edge) {
3681 return edge.source().same(root) && callingEles.has(edge);
3682 });
3683 var k_in = incoming.length;
3684 var k_out = outgoing.length;
3685 var s_in = 0;
3686 var s_out = 0; // Now, sum incoming edge weights
3687
3688 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
3689 s_in += weight(incoming[_i2]);
3690 } // Now, sum outgoing edge weights
3691
3692
3693 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
3694 s_out += weight(outgoing[_i3]);
3695 }
3696
3697 return {
3698 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
3699 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
3700 };
3701 }
3702 } // degreeCentrality
3703
3704}; // elesfn
3705// nice, short mathemathical alias
3706
3707elesfn$8.dc = elesfn$8.degreeCentrality;
3708elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
3709
3710var defaults$2 = defaults({
3711 harmonic: true,
3712 weight: function weight() {
3713 return 1;
3714 },
3715 directed: false,
3716 root: null
3717});
3718var elesfn$9 = {
3719 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
3720 var _defaults = defaults$2(options),
3721 harmonic = _defaults.harmonic,
3722 weight = _defaults.weight,
3723 directed = _defaults.directed;
3724
3725 var cy = this.cy();
3726 var closenesses = {};
3727 var maxCloseness = 0;
3728 var nodes = this.nodes();
3729 var fw = this.floydWarshall({
3730 weight: weight,
3731 directed: directed
3732 }); // Compute closeness for every node and find the maximum closeness
3733
3734 for (var i = 0; i < nodes.length; i++) {
3735 var currCloseness = 0;
3736 var node_i = nodes[i];
3737
3738 for (var j = 0; j < nodes.length; j++) {
3739 if (i !== j) {
3740 var d = fw.distance(node_i, nodes[j]);
3741
3742 if (harmonic) {
3743 currCloseness += 1 / d;
3744 } else {
3745 currCloseness += d;
3746 }
3747 }
3748 }
3749
3750 if (!harmonic) {
3751 currCloseness = 1 / currCloseness;
3752 }
3753
3754 if (maxCloseness < currCloseness) {
3755 maxCloseness = currCloseness;
3756 }
3757
3758 closenesses[node_i.id()] = currCloseness;
3759 }
3760
3761 return {
3762 closeness: function closeness(node) {
3763 if (maxCloseness == 0) {
3764 return 0;
3765 }
3766
3767 if (string(node)) {
3768 // from is a selector string
3769 node = cy.filter(node)[0].id();
3770 } else {
3771 // from is a node
3772 node = node.id();
3773 }
3774
3775 return closenesses[node] / maxCloseness;
3776 }
3777 };
3778 },
3779 // Implemented from pseudocode from wikipedia
3780 closenessCentrality: function closenessCentrality(options) {
3781 var _defaults2 = defaults$2(options),
3782 root = _defaults2.root,
3783 weight = _defaults2.weight,
3784 directed = _defaults2.directed,
3785 harmonic = _defaults2.harmonic;
3786
3787 root = this.filter(root)[0]; // we need distance from this node to every other node
3788
3789 var dijkstra = this.dijkstra({
3790 root: root,
3791 weight: weight,
3792 directed: directed
3793 });
3794 var totalDistance = 0;
3795 var nodes = this.nodes();
3796
3797 for (var i = 0; i < nodes.length; i++) {
3798 var n = nodes[i];
3799
3800 if (!n.same(root)) {
3801 var d = dijkstra.distanceTo(n);
3802
3803 if (harmonic) {
3804 totalDistance += 1 / d;
3805 } else {
3806 totalDistance += d;
3807 }
3808 }
3809 }
3810
3811 return harmonic ? totalDistance : 1 / totalDistance;
3812 } // closenessCentrality
3813
3814}; // elesfn
3815// nice, short mathemathical alias
3816
3817elesfn$9.cc = elesfn$9.closenessCentrality;
3818elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
3819
3820var defaults$3 = defaults({
3821 weight: null,
3822 directed: false
3823});
3824var elesfn$a = {
3825 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
3826 betweennessCentrality: function betweennessCentrality(options) {
3827 var _defaults = defaults$3(options),
3828 directed = _defaults.directed,
3829 weight = _defaults.weight;
3830
3831 var weighted = weight != null;
3832 var cy = this.cy(); // starting
3833
3834 var V = this.nodes();
3835 var A = {};
3836 var _C = {};
3837 var max = 0;
3838 var C = {
3839 set: function set(key, val) {
3840 _C[key] = val;
3841
3842 if (val > max) {
3843 max = val;
3844 }
3845 },
3846 get: function get(key) {
3847 return _C[key];
3848 }
3849 }; // A contains the neighborhoods of every node
3850
3851 for (var i = 0; i < V.length; i++) {
3852 var v = V[i];
3853 var vid = v.id();
3854
3855 if (directed) {
3856 A[vid] = v.outgoers().nodes(); // get outgoers of every node
3857 } else {
3858 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
3859 }
3860
3861 C.set(vid, 0);
3862 }
3863
3864 var _loop = function _loop(s) {
3865 var sid = V[s].id();
3866 var S = []; // stack
3867
3868 var P = {};
3869 var g = {};
3870 var d = {};
3871 var Q = new Heap(function (a, b) {
3872 return d[a] - d[b];
3873 }); // queue
3874 // init dictionaries
3875
3876 for (var _i = 0; _i < V.length; _i++) {
3877 var _vid = V[_i].id();
3878
3879 P[_vid] = [];
3880 g[_vid] = 0;
3881 d[_vid] = Infinity;
3882 }
3883
3884 g[sid] = 1; // sigma
3885
3886 d[sid] = 0; // distance to s
3887
3888 Q.push(sid);
3889
3890 while (!Q.empty()) {
3891 var _v = Q.pop();
3892
3893 S.push(_v);
3894
3895 if (weighted) {
3896 for (var j = 0; j < A[_v].length; j++) {
3897 var w = A[_v][j];
3898 var vEle = cy.getElementById(_v);
3899 var edge = void 0;
3900
3901 if (vEle.edgesTo(w).length > 0) {
3902 edge = vEle.edgesTo(w)[0];
3903 } else {
3904 edge = w.edgesTo(vEle)[0];
3905 }
3906
3907 var edgeWeight = weight(edge);
3908 w = w.id();
3909
3910 if (d[w] > d[_v] + edgeWeight) {
3911 d[w] = d[_v] + edgeWeight;
3912
3913 if (Q.nodes.indexOf(w) < 0) {
3914 //if w is not in Q
3915 Q.push(w);
3916 } else {
3917 // update position if w is in Q
3918 Q.updateItem(w);
3919 }
3920
3921 g[w] = 0;
3922 P[w] = [];
3923 }
3924
3925 if (d[w] == d[_v] + edgeWeight) {
3926 g[w] = g[w] + g[_v];
3927 P[w].push(_v);
3928 }
3929 }
3930 } else {
3931 for (var _j = 0; _j < A[_v].length; _j++) {
3932 var _w = A[_v][_j].id();
3933
3934 if (d[_w] == Infinity) {
3935 Q.push(_w);
3936 d[_w] = d[_v] + 1;
3937 }
3938
3939 if (d[_w] == d[_v] + 1) {
3940 g[_w] = g[_w] + g[_v];
3941
3942 P[_w].push(_v);
3943 }
3944 }
3945 }
3946 }
3947
3948 var e = {};
3949
3950 for (var _i2 = 0; _i2 < V.length; _i2++) {
3951 e[V[_i2].id()] = 0;
3952 }
3953
3954 while (S.length > 0) {
3955 var _w2 = S.pop();
3956
3957 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
3958 var _v2 = P[_w2][_j2];
3959 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
3960
3961 if (_w2 != V[s].id()) {
3962 C.set(_w2, C.get(_w2) + e[_w2]);
3963 }
3964 }
3965 }
3966 };
3967
3968 for (var s = 0; s < V.length; s++) {
3969 _loop(s);
3970 }
3971
3972 var ret = {
3973 betweenness: function betweenness(node) {
3974 var id = cy.collection(node).id();
3975 return C.get(id);
3976 },
3977 betweennessNormalized: function betweennessNormalized(node) {
3978 if (max == 0) {
3979 return 0;
3980 }
3981
3982 var id = cy.collection(node).id();
3983 return C.get(id) / max;
3984 }
3985 }; // alias
3986
3987 ret.betweennessNormalised = ret.betweennessNormalized;
3988 return ret;
3989 } // betweennessCentrality
3990
3991}; // elesfn
3992// nice, short mathemathical alias
3993
3994elesfn$a.bc = elesfn$a.betweennessCentrality;
3995
3996// Implemented by Zoe Xi @zoexi for GSOC 2016
3997/* eslint-disable no-unused-vars */
3998
3999var defaults$4 = defaults({
4000 expandFactor: 2,
4001 // affects time of computation and cluster granularity to some extent: M * M
4002 inflateFactor: 2,
4003 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4004 multFactor: 1,
4005 // optional self loops for each node. Use a neutral value to improve cluster computations.
4006 maxIterations: 20,
4007 // maximum number of iterations of the MCL algorithm in a single run
4008 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4009 function (edge) {
4010 return 1;
4011 }]
4012});
4013/* eslint-enable */
4014
4015var setOptions = function setOptions(options) {
4016 return defaults$4(options);
4017};
4018/* eslint-enable */
4019
4020
4021var getSimilarity = function getSimilarity(edge, attributes) {
4022 var total = 0;
4023
4024 for (var i = 0; i < attributes.length; i++) {
4025 total += attributes[i](edge);
4026 }
4027
4028 return total;
4029};
4030
4031var addLoops = function addLoops(M, n, val) {
4032 for (var i = 0; i < n; i++) {
4033 M[i * n + i] = val;
4034 }
4035};
4036
4037var normalize = function normalize(M, n) {
4038 var sum;
4039
4040 for (var col = 0; col < n; col++) {
4041 sum = 0;
4042
4043 for (var row = 0; row < n; row++) {
4044 sum += M[row * n + col];
4045 }
4046
4047 for (var _row = 0; _row < n; _row++) {
4048 M[_row * n + col] = M[_row * n + col] / sum;
4049 }
4050 }
4051}; // TODO: blocked matrix multiplication?
4052
4053
4054var mmult = function mmult(A, B, n) {
4055 var C = new Array(n * n);
4056
4057 for (var i = 0; i < n; i++) {
4058 for (var j = 0; j < n; j++) {
4059 C[i * n + j] = 0;
4060 }
4061
4062 for (var k = 0; k < n; k++) {
4063 for (var _j = 0; _j < n; _j++) {
4064 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4065 }
4066 }
4067 }
4068
4069 return C;
4070};
4071
4072var expand = function expand(M, n, expandFactor
4073/** power **/
4074) {
4075 var _M = M.slice(0);
4076
4077 for (var p = 1; p < expandFactor; p++) {
4078 M = mmult(M, _M, n);
4079 }
4080
4081 return M;
4082};
4083
4084var inflate = function inflate(M, n, inflateFactor
4085/** r **/
4086) {
4087 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4088
4089
4090 for (var i = 0; i < n * n; i++) {
4091 _M[i] = Math.pow(M[i], inflateFactor);
4092 }
4093
4094 normalize(_M, n);
4095 return _M;
4096};
4097
4098var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4099 // Check that both matrices have the same elements (i,j)
4100 for (var i = 0; i < n2; i++) {
4101 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4102
4103 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4104
4105 if (v1 !== v2) {
4106 return false;
4107 }
4108 }
4109
4110 return true;
4111};
4112
4113var assign = function assign(M, n, nodes, cy) {
4114 var clusters = [];
4115
4116 for (var i = 0; i < n; i++) {
4117 var cluster = [];
4118
4119 for (var j = 0; j < n; j++) {
4120 // Row-wise attractors and elements that they attract belong in same cluster
4121 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4122 cluster.push(nodes[j]);
4123 }
4124 }
4125
4126 if (cluster.length !== 0) {
4127 clusters.push(cy.collection(cluster));
4128 }
4129 }
4130
4131 return clusters;
4132};
4133
4134var isDuplicate = function isDuplicate(c1, c2) {
4135 for (var i = 0; i < c1.length; i++) {
4136 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4137 return false;
4138 }
4139 }
4140
4141 return true;
4142};
4143
4144var removeDuplicates = function removeDuplicates(clusters) {
4145 for (var i = 0; i < clusters.length; i++) {
4146 for (var j = 0; j < clusters.length; j++) {
4147 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4148 clusters.splice(j, 1);
4149 }
4150 }
4151 }
4152
4153 return clusters;
4154};
4155
4156var markovClustering = function markovClustering(options) {
4157 var nodes = this.nodes();
4158 var edges = this.edges();
4159 var cy = this.cy(); // Set parameters of algorithm:
4160
4161 var opts = setOptions(options); // Map each node to its position in node array
4162
4163 var id2position = {};
4164
4165 for (var i = 0; i < nodes.length; i++) {
4166 id2position[nodes[i].id()] = i;
4167 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4168
4169
4170 var n = nodes.length,
4171 n2 = n * n;
4172
4173 var M = new Array(n2),
4174 _M;
4175
4176 for (var _i = 0; _i < n2; _i++) {
4177 M[_i] = 0;
4178 }
4179
4180 for (var e = 0; e < edges.length; e++) {
4181 var edge = edges[e];
4182 var _i2 = id2position[edge.source().id()];
4183 var j = id2position[edge.target().id()];
4184 var sim = getSimilarity(edge, opts.attributes);
4185 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4186
4187 M[j * n + _i2] += sim;
4188 } // Begin Markov cluster algorithm
4189 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4190
4191
4192 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4193
4194 normalize(M, n);
4195 var isStillMoving = true;
4196 var iterations = 0;
4197
4198 while (isStillMoving && iterations < opts.maxIterations) {
4199 isStillMoving = false; // Step 3:
4200
4201 _M = expand(M, n, opts.expandFactor); // Step 4:
4202
4203 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4204
4205 if (!hasConverged(M, _M, n2, 4)) {
4206 isStillMoving = true;
4207 }
4208
4209 iterations++;
4210 } // Build clusters from matrix
4211
4212
4213 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4214
4215 clusters = removeDuplicates(clusters);
4216 return clusters;
4217};
4218
4219var markovClustering$1 = {
4220 markovClustering: markovClustering,
4221 mcl: markovClustering
4222};
4223
4224// Common distance metrics for clustering algorithms
4225
4226var identity = function identity(x) {
4227 return x;
4228};
4229
4230var absDiff = function absDiff(p, q) {
4231 return Math.abs(q - p);
4232};
4233
4234var addAbsDiff = function addAbsDiff(total, p, q) {
4235 return total + absDiff(p, q);
4236};
4237
4238var addSquaredDiff = function addSquaredDiff(total, p, q) {
4239 return total + Math.pow(q - p, 2);
4240};
4241
4242var sqrt = function sqrt(x) {
4243 return Math.sqrt(x);
4244};
4245
4246var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4247 return Math.max(currentMax, absDiff(p, q));
4248};
4249
4250var getDistance = function getDistance(length, getP, getQ, init, visit) {
4251 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4252 var ret = init;
4253 var p, q;
4254
4255 for (var dim = 0; dim < length; dim++) {
4256 p = getP(dim);
4257 q = getQ(dim);
4258 ret = visit(ret, p, q);
4259 }
4260
4261 return post(ret);
4262};
4263
4264var distances = {
4265 euclidean: function euclidean(length, getP, getQ) {
4266 if (length >= 2) {
4267 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4268 } else {
4269 // for single attr case, more efficient to avoid sqrt
4270 return getDistance(length, getP, getQ, 0, addAbsDiff);
4271 }
4272 },
4273 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4274 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4275 },
4276 manhattan: function manhattan(length, getP, getQ) {
4277 return getDistance(length, getP, getQ, 0, addAbsDiff);
4278 },
4279 max: function max(length, getP, getQ) {
4280 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4281 }
4282}; // in case the user accidentally doesn't use camel case
4283
4284distances['squared-euclidean'] = distances['squaredEuclidean'];
4285distances['squaredeuclidean'] = distances['squaredEuclidean'];
4286function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4287 var impl;
4288
4289 if (fn(method)) {
4290 impl = method;
4291 } else {
4292 impl = distances[method] || distances.euclidean;
4293 }
4294
4295 if (length === 0 && fn(method)) {
4296 return impl(nodeP, nodeQ);
4297 } else {
4298 return impl(length, getP, getQ, nodeP, nodeQ);
4299 }
4300}
4301
4302var defaults$5 = defaults({
4303 k: 2,
4304 m: 2,
4305 sensitivityThreshold: 0.0001,
4306 distance: 'euclidean',
4307 maxIterations: 10,
4308 attributes: [],
4309 testMode: false,
4310 testCentroids: null
4311});
4312
4313var setOptions$1 = function setOptions(options) {
4314 return defaults$5(options);
4315};
4316/* eslint-enable */
4317
4318
4319var getDist = function getDist(type, node, centroid, attributes, mode) {
4320 var noNodeP = mode !== 'kMedoids';
4321 var getP = noNodeP ? function (i) {
4322 return centroid[i];
4323 } : function (i) {
4324 return attributes[i](centroid);
4325 };
4326
4327 var getQ = function getQ(i) {
4328 return attributes[i](node);
4329 };
4330
4331 var nodeP = centroid;
4332 var nodeQ = node;
4333 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4334};
4335
4336var randomCentroids = function randomCentroids(nodes, k, attributes) {
4337 var ndim = attributes.length;
4338 var min = new Array(ndim);
4339 var max = new Array(ndim);
4340 var centroids = new Array(k);
4341 var centroid = null; // Find min, max values for each attribute dimension
4342
4343 for (var i = 0; i < ndim; i++) {
4344 min[i] = nodes.min(attributes[i]).value;
4345 max[i] = nodes.max(attributes[i]).value;
4346 } // Build k centroids, each represented as an n-dim feature vector
4347
4348
4349 for (var c = 0; c < k; c++) {
4350 centroid = [];
4351
4352 for (var _i = 0; _i < ndim; _i++) {
4353 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4354 }
4355
4356 centroids[c] = centroid;
4357 }
4358
4359 return centroids;
4360};
4361
4362var classify = function classify(node, centroids, distance, attributes, type) {
4363 var min = Infinity;
4364 var index = 0;
4365
4366 for (var i = 0; i < centroids.length; i++) {
4367 var dist = getDist(distance, node, centroids[i], attributes, type);
4368
4369 if (dist < min) {
4370 min = dist;
4371 index = i;
4372 }
4373 }
4374
4375 return index;
4376};
4377
4378var buildCluster = function buildCluster(centroid, nodes, assignment) {
4379 var cluster = [];
4380 var node = null;
4381
4382 for (var n = 0; n < nodes.length; n++) {
4383 node = nodes[n];
4384
4385 if (assignment[node.id()] === centroid) {
4386 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4387 cluster.push(node);
4388 }
4389 }
4390
4391 return cluster;
4392};
4393
4394var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4395 return Math.abs(v2 - v1) <= sensitivityThreshold;
4396};
4397
4398var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4399 for (var i = 0; i < v1.length; i++) {
4400 for (var j = 0; j < v1[i].length; j++) {
4401 var diff = Math.abs(v1[i][j] - v2[i][j]);
4402
4403 if (diff > sensitivityThreshold) {
4404 return false;
4405 }
4406 }
4407 }
4408
4409 return true;
4410};
4411
4412var seenBefore = function seenBefore(node, medoids, n) {
4413 for (var i = 0; i < n; i++) {
4414 if (node === medoids[i]) return true;
4415 }
4416
4417 return false;
4418};
4419
4420var randomMedoids = function randomMedoids(nodes, k) {
4421 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
4422 // so we need to check to see if we've already seen or chose this node before.
4423
4424 if (nodes.length < 50) {
4425 // Randomly select k medoids from the n nodes
4426 for (var i = 0; i < k; i++) {
4427 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).
4428 // Instead choose a different random node.
4429
4430 while (seenBefore(node, medoids, i)) {
4431 node = nodes[Math.floor(Math.random() * nodes.length)];
4432 }
4433
4434 medoids[i] = node;
4435 }
4436 } else {
4437 // Relatively large data set, so pretty safe to not check and just select random nodes
4438 for (var _i2 = 0; _i2 < k; _i2++) {
4439 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4440 }
4441 }
4442
4443 return medoids;
4444};
4445
4446var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4447 var cost = 0;
4448
4449 for (var n = 0; n < cluster.length; n++) {
4450 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4451 }
4452
4453 return cost;
4454};
4455
4456var kMeans = function kMeans(options) {
4457 var cy = this.cy();
4458 var nodes = this.nodes();
4459 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
4460
4461 var opts = setOptions$1(options); // Begin k-means algorithm
4462
4463 var clusters = new Array(opts.k);
4464 var assignment = {};
4465 var centroids; // Step 1: Initialize centroid positions
4466
4467 if (opts.testMode) {
4468 if (typeof opts.testCentroids === 'number') {
4469 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4470 } else if (_typeof(opts.testCentroids) === 'object') {
4471 centroids = opts.testCentroids;
4472 } else {
4473 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4474 }
4475 } else {
4476 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4477 }
4478
4479 var isStillMoving = true;
4480 var iterations = 0;
4481
4482 while (isStillMoving && iterations < opts.maxIterations) {
4483 // Step 2: Assign nodes to the nearest centroid
4484 for (var n = 0; n < nodes.length; n++) {
4485 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4486
4487 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
4488 } // Step 3: For each of the k clusters, update its centroid
4489
4490
4491 isStillMoving = false;
4492
4493 for (var c = 0; c < opts.k; c++) {
4494 // Get all nodes that belong to this cluster
4495 var cluster = buildCluster(c, nodes, assignment);
4496
4497 if (cluster.length === 0) {
4498 // If cluster is empty, break out early & move to next cluster
4499 continue;
4500 } // Update centroids by calculating avg of all nodes within the cluster.
4501
4502
4503 var ndim = opts.attributes.length;
4504 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
4505
4506 var newCentroid = new Array(ndim);
4507 var sum = new Array(ndim);
4508
4509 for (var d = 0; d < ndim; d++) {
4510 sum[d] = 0.0;
4511
4512 for (var i = 0; i < cluster.length; i++) {
4513 node = cluster[i];
4514 sum[d] += opts.attributes[d](node);
4515 }
4516
4517 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
4518
4519 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
4520 isStillMoving = true;
4521 }
4522 }
4523
4524 centroids[c] = newCentroid;
4525 clusters[c] = cy.collection(cluster);
4526 }
4527
4528 iterations++;
4529 }
4530
4531 return clusters;
4532};
4533
4534var kMedoids = function kMedoids(options) {
4535 var cy = this.cy();
4536 var nodes = this.nodes();
4537 var node = null;
4538 var opts = setOptions$1(options); // Begin k-medoids algorithm
4539
4540 var clusters = new Array(opts.k);
4541 var medoids;
4542 var assignment = {};
4543 var curCost;
4544 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
4545 // Step 1: Initialize k medoids
4546
4547 if (opts.testMode) {
4548 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
4549 medoids = opts.testCentroids;
4550 } else {
4551 medoids = randomMedoids(nodes, opts.k);
4552 }
4553 } else {
4554 medoids = randomMedoids(nodes, opts.k);
4555 }
4556
4557 var isStillMoving = true;
4558 var iterations = 0;
4559
4560 while (isStillMoving && iterations < opts.maxIterations) {
4561 // Step 2: Assign nodes to the nearest medoid
4562 for (var n = 0; n < nodes.length; n++) {
4563 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4564
4565 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
4566 }
4567
4568 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
4569 // select the node with the lowest configuration cost as new medoid.
4570
4571 for (var m = 0; m < medoids.length; m++) {
4572 // Get all nodes that belong to this medoid
4573 var cluster = buildCluster(m, nodes, assignment);
4574
4575 if (cluster.length === 0) {
4576 // If cluster is empty, break out early & move to next cluster
4577 continue;
4578 }
4579
4580 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
4581 // Select different medoid if its configuration has the lowest cost
4582
4583 for (var _n = 0; _n < cluster.length; _n++) {
4584 curCost = findCost(cluster[_n], cluster, opts.attributes);
4585
4586 if (curCost < minCosts[m]) {
4587 minCosts[m] = curCost;
4588 medoids[m] = cluster[_n];
4589 isStillMoving = true;
4590 }
4591 }
4592
4593 clusters[m] = cy.collection(cluster);
4594 }
4595
4596 iterations++;
4597 }
4598
4599 return clusters;
4600};
4601
4602var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
4603 var numerator, denominator;
4604
4605 for (var n = 0; n < nodes.length; n++) {
4606 for (var c = 0; c < centroids.length; c++) {
4607 weight[n][c] = Math.pow(U[n][c], opts.m);
4608 }
4609 }
4610
4611 for (var _c = 0; _c < centroids.length; _c++) {
4612 for (var dim = 0; dim < opts.attributes.length; dim++) {
4613 numerator = 0;
4614 denominator = 0;
4615
4616 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
4617 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
4618 denominator += weight[_n2][_c];
4619 }
4620
4621 centroids[_c][dim] = numerator / denominator;
4622 }
4623 }
4624};
4625
4626var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
4627 // Save previous step
4628 for (var i = 0; i < U.length; i++) {
4629 _U[i] = U[i].slice();
4630 }
4631
4632 var sum, numerator, denominator;
4633 var pow = 2 / (opts.m - 1);
4634
4635 for (var c = 0; c < centroids.length; c++) {
4636 for (var n = 0; n < nodes.length; n++) {
4637 sum = 0;
4638
4639 for (var k = 0; k < centroids.length; k++) {
4640 // against all other centroids
4641 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
4642 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
4643 sum += Math.pow(numerator / denominator, pow);
4644 }
4645
4646 U[n][c] = 1 / sum;
4647 }
4648 }
4649};
4650
4651var assign$1 = function assign(nodes, U, opts, cy) {
4652 var clusters = new Array(opts.k);
4653
4654 for (var c = 0; c < clusters.length; c++) {
4655 clusters[c] = [];
4656 }
4657
4658 var max;
4659 var index;
4660
4661 for (var n = 0; n < U.length; n++) {
4662 // for each node (U is N x C matrix)
4663 max = -Infinity;
4664 index = -1; // Determine which cluster the node is most likely to belong in
4665
4666 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
4667 if (U[n][_c2] > max) {
4668 max = U[n][_c2];
4669 index = _c2;
4670 }
4671 }
4672
4673 clusters[index].push(nodes[n]);
4674 } // Turn every array into a collection of nodes
4675
4676
4677 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
4678 clusters[_c3] = cy.collection(clusters[_c3]);
4679 }
4680
4681 return clusters;
4682};
4683
4684var fuzzyCMeans = function fuzzyCMeans(options) {
4685 var cy = this.cy();
4686 var nodes = this.nodes();
4687 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
4688
4689 var clusters;
4690 var centroids;
4691 var U;
4692
4693 var _U;
4694
4695 var weight; // Step 1: Initialize letiables.
4696
4697 _U = new Array(nodes.length);
4698
4699 for (var i = 0; i < nodes.length; i++) {
4700 // N x C matrix
4701 _U[i] = new Array(opts.k);
4702 }
4703
4704 U = new Array(nodes.length);
4705
4706 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
4707 // N x C matrix
4708 U[_i3] = new Array(opts.k);
4709 }
4710
4711 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
4712 var total = 0;
4713
4714 for (var j = 0; j < opts.k; j++) {
4715 U[_i4][j] = Math.random();
4716 total += U[_i4][j];
4717 }
4718
4719 for (var _j = 0; _j < opts.k; _j++) {
4720 U[_i4][_j] = U[_i4][_j] / total;
4721 }
4722 }
4723
4724 centroids = new Array(opts.k);
4725
4726 for (var _i5 = 0; _i5 < opts.k; _i5++) {
4727 centroids[_i5] = new Array(opts.attributes.length);
4728 }
4729
4730 weight = new Array(nodes.length);
4731
4732 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
4733 // N x C matrix
4734 weight[_i6] = new Array(opts.k);
4735 } // end init FCM
4736
4737
4738 var isStillMoving = true;
4739 var iterations = 0;
4740
4741 while (isStillMoving && iterations < opts.maxIterations) {
4742 isStillMoving = false; // Step 2: Calculate the centroids for each step.
4743
4744 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
4745
4746 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
4747
4748 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
4749 isStillMoving = true;
4750 }
4751
4752 iterations++;
4753 } // Assign nodes to clusters with highest probability.
4754
4755
4756 clusters = assign$1(nodes, U, opts, cy);
4757 return {
4758 clusters: clusters,
4759 degreeOfMembership: U
4760 };
4761};
4762
4763var kClustering = {
4764 kMeans: kMeans,
4765 kMedoids: kMedoids,
4766 fuzzyCMeans: fuzzyCMeans,
4767 fcm: fuzzyCMeans
4768};
4769
4770// Implemented by Zoe Xi @zoexi for GSOC 2016
4771var defaults$6 = defaults({
4772 distance: 'euclidean',
4773 // distance metric to compare nodes
4774 linkage: 'min',
4775 // linkage criterion : how to determine the distance between clusters of nodes
4776 mode: 'threshold',
4777 // mode:'threshold' => clusters must be threshold distance apart
4778 threshold: Infinity,
4779 // the distance threshold
4780 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
4781 addDendrogram: false,
4782 // whether to add the dendrogram to the graph for viz
4783 dendrogramDepth: 0,
4784 // depth at which dendrogram branches are merged into the returned clusters
4785 attributes: [] // array of attr functions
4786
4787});
4788var linkageAliases = {
4789 'single': 'min',
4790 'complete': 'max'
4791};
4792
4793var setOptions$2 = function setOptions(options) {
4794 var opts = defaults$6(options);
4795 var preferredAlias = linkageAliases[opts.linkage];
4796
4797 if (preferredAlias != null) {
4798 opts.linkage = preferredAlias;
4799 }
4800
4801 return opts;
4802};
4803
4804var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
4805 // Find two closest clusters from cached mins
4806 var minKey = 0;
4807 var min = Infinity;
4808 var dist;
4809 var attrs = opts.attributes;
4810
4811 var getDist = function getDist(n1, n2) {
4812 return clusteringDistance(opts.distance, attrs.length, function (i) {
4813 return attrs[i](n1);
4814 }, function (i) {
4815 return attrs[i](n2);
4816 }, n1, n2);
4817 };
4818
4819 for (var i = 0; i < clusters.length; i++) {
4820 var key = clusters[i].key;
4821 var _dist = dists[key][mins[key]];
4822
4823 if (_dist < min) {
4824 minKey = key;
4825 min = _dist;
4826 }
4827 }
4828
4829 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
4830 return false;
4831 }
4832
4833 var c1 = index[minKey];
4834 var c2 = index[mins[minKey]];
4835 var merged; // Merge two closest clusters
4836
4837 if (opts.mode === 'dendrogram') {
4838 merged = {
4839 left: c1,
4840 right: c2,
4841 key: c1.key
4842 };
4843 } else {
4844 merged = {
4845 value: c1.value.concat(c2.value),
4846 key: c1.key
4847 };
4848 }
4849
4850 clusters[c1.index] = merged;
4851 clusters.splice(c2.index, 1);
4852 index[c1.key] = merged; // Update distances with new merged cluster
4853
4854 for (var _i = 0; _i < clusters.length; _i++) {
4855 var cur = clusters[_i];
4856
4857 if (c1.key === cur.key) {
4858 dist = Infinity;
4859 } else if (opts.linkage === 'min') {
4860 dist = dists[c1.key][cur.key];
4861
4862 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
4863 dist = dists[c2.key][cur.key];
4864 }
4865 } else if (opts.linkage === 'max') {
4866 dist = dists[c1.key][cur.key];
4867
4868 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
4869 dist = dists[c2.key][cur.key];
4870 }
4871 } else if (opts.linkage === 'mean') {
4872 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
4873 } else {
4874 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
4875 }
4876
4877 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
4878 } // Update cached mins
4879
4880
4881 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
4882 var key1 = clusters[_i2].key;
4883
4884 if (mins[key1] === c1.key || mins[key1] === c2.key) {
4885 var _min = key1;
4886
4887 for (var j = 0; j < clusters.length; j++) {
4888 var key2 = clusters[j].key;
4889
4890 if (dists[key1][key2] < dists[key1][_min]) {
4891 _min = key2;
4892 }
4893 }
4894
4895 mins[key1] = _min;
4896 }
4897
4898 clusters[_i2].index = _i2;
4899 } // Clean up meta data used for clustering
4900
4901
4902 c1.key = c2.key = c1.index = c2.index = null;
4903 return true;
4904};
4905
4906var getAllChildren = function getAllChildren(root, arr, cy) {
4907 if (!root) return;
4908
4909 if (root.value) {
4910 arr.push(root.value);
4911 } else {
4912 if (root.left) getAllChildren(root.left, arr);
4913 if (root.right) getAllChildren(root.right, arr);
4914 }
4915};
4916
4917var buildDendrogram = function buildDendrogram(root, cy) {
4918 if (!root) return '';
4919
4920 if (root.left && root.right) {
4921 var leftStr = buildDendrogram(root.left, cy);
4922 var rightStr = buildDendrogram(root.right, cy);
4923 var node = cy.add({
4924 group: 'nodes',
4925 data: {
4926 id: leftStr + ',' + rightStr
4927 }
4928 });
4929 cy.add({
4930 group: 'edges',
4931 data: {
4932 source: leftStr,
4933 target: node.id()
4934 }
4935 });
4936 cy.add({
4937 group: 'edges',
4938 data: {
4939 source: rightStr,
4940 target: node.id()
4941 }
4942 });
4943 return node.id();
4944 } else if (root.value) {
4945 return root.value.id();
4946 }
4947};
4948
4949var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
4950 if (!root) return [];
4951 var left = [],
4952 right = [],
4953 leaves = [];
4954
4955 if (k === 0) {
4956 // don't cut tree, simply return all nodes as 1 single cluster
4957 if (root.left) getAllChildren(root.left, left);
4958 if (root.right) getAllChildren(root.right, right);
4959 leaves = left.concat(right);
4960 return [cy.collection(leaves)];
4961 } else if (k === 1) {
4962 // cut at root
4963 if (root.value) {
4964 // leaf node
4965 return [cy.collection(root.value)];
4966 } else {
4967 if (root.left) getAllChildren(root.left, left);
4968 if (root.right) getAllChildren(root.right, right);
4969 return [cy.collection(left), cy.collection(right)];
4970 }
4971 } else {
4972 if (root.value) {
4973 return [cy.collection(root.value)];
4974 } else {
4975 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
4976 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
4977 return left.concat(right);
4978 }
4979 }
4980};
4981/* eslint-enable */
4982
4983
4984var hierarchicalClustering = function hierarchicalClustering(options) {
4985 var cy = this.cy();
4986 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
4987
4988 var opts = setOptions$2(options);
4989 var attrs = opts.attributes;
4990
4991 var getDist = function getDist(n1, n2) {
4992 return clusteringDistance(opts.distance, attrs.length, function (i) {
4993 return attrs[i](n1);
4994 }, function (i) {
4995 return attrs[i](n2);
4996 }, n1, n2);
4997 }; // Begin hierarchical algorithm
4998
4999
5000 var clusters = [];
5001 var dists = []; // distances between each pair of clusters
5002
5003 var mins = []; // closest cluster for each cluster
5004
5005 var index = []; // hash of all clusters by key
5006 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5007
5008 for (var n = 0; n < nodes.length; n++) {
5009 var cluster = {
5010 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5011 key: n,
5012 index: n
5013 };
5014 clusters[n] = cluster;
5015 index[n] = cluster;
5016 dists[n] = [];
5017 mins[n] = 0;
5018 } // Calculate the distance between each pair of clusters
5019
5020
5021 for (var i = 0; i < clusters.length; i++) {
5022 for (var j = 0; j <= i; j++) {
5023 var dist = void 0;
5024
5025 if (opts.mode === 'dendrogram') {
5026 // modes store cluster values differently
5027 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5028 } else {
5029 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5030 }
5031
5032 dists[i][j] = dist;
5033 dists[j][i] = dist;
5034
5035 if (dist < dists[i][mins[i]]) {
5036 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5037 }
5038 }
5039 } // Find the closest pair of clusters and merge them into a single cluster.
5040 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5041
5042
5043 var merged = mergeClosest(clusters, index, dists, mins, opts);
5044
5045 while (merged) {
5046 merged = mergeClosest(clusters, index, dists, mins, opts);
5047 }
5048
5049 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5050 // in addition to returning the clusters.
5051
5052 if (opts.mode === 'dendrogram') {
5053 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5054 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5055 } else {
5056 // Regular mode simply returns the clusters
5057 retClusters = new Array(clusters.length);
5058 clusters.forEach(function (cluster, i) {
5059 // Clean up meta data used for clustering
5060 cluster.key = cluster.index = null;
5061 retClusters[i] = cy.collection(cluster.value);
5062 });
5063 }
5064
5065 return retClusters;
5066};
5067
5068var hierarchicalClustering$1 = {
5069 hierarchicalClustering: hierarchicalClustering,
5070 hca: hierarchicalClustering
5071};
5072
5073// Implemented by Zoe Xi @zoexi for GSOC 2016
5074var defaults$7 = defaults({
5075 distance: 'euclidean',
5076 // distance metric to compare attributes between two nodes
5077 preference: 'median',
5078 // suitability of a data point to serve as an exemplar
5079 damping: 0.8,
5080 // damping factor between [0.5, 1)
5081 maxIterations: 1000,
5082 // max number of iterations to run
5083 minIterations: 100,
5084 // min number of iterations to run in order for clustering to stop
5085 attributes: [// functions to quantify the similarity between any two points
5086 // e.g. node => node.data('weight')
5087 ]
5088});
5089
5090var setOptions$3 = function setOptions(options) {
5091 var dmp = options.damping;
5092 var pref = options.preference;
5093
5094 if (!(0.5 <= dmp && dmp < 1)) {
5095 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5096 }
5097
5098 var validPrefs = ['median', 'mean', 'min', 'max'];
5099
5100 if (!(validPrefs.some(function (v) {
5101 return v === pref;
5102 }) || number(pref))) {
5103 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5104 return "'".concat(p, "'");
5105 }).join(', '), "] or a number. Got: ").concat(pref));
5106 }
5107
5108 return defaults$7(options);
5109};
5110/* eslint-enable */
5111
5112
5113var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5114 var attr = function attr(n, i) {
5115 return attributes[i](n);
5116 }; // nb negative because similarity should have an inverse relationship to distance
5117
5118
5119 return -clusteringDistance(type, attributes.length, function (i) {
5120 return attr(n1, i);
5121 }, function (i) {
5122 return attr(n2, i);
5123 }, n1, n2);
5124};
5125
5126var getPreference = function getPreference(S, preference) {
5127 // larger preference = greater # of clusters
5128 var p = null;
5129
5130 if (preference === 'median') {
5131 p = median(S);
5132 } else if (preference === 'mean') {
5133 p = mean(S);
5134 } else if (preference === 'min') {
5135 p = min(S);
5136 } else if (preference === 'max') {
5137 p = max(S);
5138 } else {
5139 // Custom preference number, as set by user
5140 p = preference;
5141 }
5142
5143 return p;
5144};
5145
5146var findExemplars = function findExemplars(n, R, A) {
5147 var indices = [];
5148
5149 for (var i = 0; i < n; i++) {
5150 if (R[i * n + i] + A[i * n + i] > 0) {
5151 indices.push(i);
5152 }
5153 }
5154
5155 return indices;
5156};
5157
5158var assignClusters = function assignClusters(n, S, exemplars) {
5159 var clusters = [];
5160
5161 for (var i = 0; i < n; i++) {
5162 var index = -1;
5163 var max = -Infinity;
5164
5165 for (var ei = 0; ei < exemplars.length; ei++) {
5166 var e = exemplars[ei];
5167
5168 if (S[i * n + e] > max) {
5169 index = e;
5170 max = S[i * n + e];
5171 }
5172 }
5173
5174 if (index > 0) {
5175 clusters.push(index);
5176 }
5177 }
5178
5179 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5180 clusters[exemplars[_ei]] = exemplars[_ei];
5181 }
5182
5183 return clusters;
5184};
5185
5186var assign$2 = function assign(n, S, exemplars) {
5187 var clusters = assignClusters(n, S, exemplars);
5188
5189 for (var ei = 0; ei < exemplars.length; ei++) {
5190 var ii = [];
5191
5192 for (var c = 0; c < clusters.length; c++) {
5193 if (clusters[c] === exemplars[ei]) {
5194 ii.push(c);
5195 }
5196 }
5197
5198 var maxI = -1;
5199 var maxSum = -Infinity;
5200
5201 for (var i = 0; i < ii.length; i++) {
5202 var sum = 0;
5203
5204 for (var j = 0; j < ii.length; j++) {
5205 sum += S[ii[j] * n + ii[i]];
5206 }
5207
5208 if (sum > maxSum) {
5209 maxI = i;
5210 maxSum = sum;
5211 }
5212 }
5213
5214 exemplars[ei] = ii[maxI];
5215 }
5216
5217 clusters = assignClusters(n, S, exemplars);
5218 return clusters;
5219};
5220
5221var affinityPropagation = function affinityPropagation(options) {
5222 var cy = this.cy();
5223 var nodes = this.nodes();
5224 var opts = setOptions$3(options); // Map each node to its position in node array
5225
5226 var id2position = {};
5227
5228 for (var i = 0; i < nodes.length; i++) {
5229 id2position[nodes[i].id()] = i;
5230 } // Begin affinity propagation algorithm
5231
5232
5233 var n; // number of data points
5234
5235 var n2; // size of matrices
5236
5237 var S; // similarity matrix (1D array)
5238
5239 var p; // preference/suitability of a data point to serve as an exemplar
5240
5241 var R; // responsibility matrix (1D array)
5242
5243 var A; // availability matrix (1D array)
5244
5245 n = nodes.length;
5246 n2 = n * n; // Initialize and build S similarity matrix
5247
5248 S = new Array(n2);
5249
5250 for (var _i = 0; _i < n2; _i++) {
5251 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5252 }
5253
5254 for (var _i2 = 0; _i2 < n; _i2++) {
5255 for (var j = 0; j < n; j++) {
5256 if (_i2 !== j) {
5257 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5258 }
5259 }
5260 } // Place preferences on the diagonal of S
5261
5262
5263 p = getPreference(S, opts.preference);
5264
5265 for (var _i3 = 0; _i3 < n; _i3++) {
5266 S[_i3 * n + _i3] = p;
5267 } // Initialize R responsibility matrix
5268
5269
5270 R = new Array(n2);
5271
5272 for (var _i4 = 0; _i4 < n2; _i4++) {
5273 R[_i4] = 0.0;
5274 } // Initialize A availability matrix
5275
5276
5277 A = new Array(n2);
5278
5279 for (var _i5 = 0; _i5 < n2; _i5++) {
5280 A[_i5] = 0.0;
5281 }
5282
5283 var old = new Array(n);
5284 var Rp = new Array(n);
5285 var se = new Array(n);
5286
5287 for (var _i6 = 0; _i6 < n; _i6++) {
5288 old[_i6] = 0.0;
5289 Rp[_i6] = 0.0;
5290 se[_i6] = 0;
5291 }
5292
5293 var e = new Array(n * opts.minIterations);
5294
5295 for (var _i7 = 0; _i7 < e.length; _i7++) {
5296 e[_i7] = 0;
5297 }
5298
5299 var iter;
5300
5301 for (iter = 0; iter < opts.maxIterations; iter++) {
5302 // main algorithmic loop
5303 // Update R responsibility matrix
5304 for (var _i8 = 0; _i8 < n; _i8++) {
5305 var max = -Infinity,
5306 max2 = -Infinity,
5307 maxI = -1,
5308 AS = 0.0;
5309
5310 for (var _j = 0; _j < n; _j++) {
5311 old[_j] = R[_i8 * n + _j];
5312 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5313
5314 if (AS >= max) {
5315 max2 = max;
5316 max = AS;
5317 maxI = _j;
5318 } else if (AS > max2) {
5319 max2 = AS;
5320 }
5321 }
5322
5323 for (var _j2 = 0; _j2 < n; _j2++) {
5324 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5325 }
5326
5327 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5328 } // Update A availability matrix
5329
5330
5331 for (var _i9 = 0; _i9 < n; _i9++) {
5332 var sum = 0;
5333
5334 for (var _j3 = 0; _j3 < n; _j3++) {
5335 old[_j3] = A[_j3 * n + _i9];
5336 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5337 sum += Rp[_j3];
5338 }
5339
5340 sum -= Rp[_i9];
5341 Rp[_i9] = R[_i9 * n + _i9];
5342 sum += Rp[_i9];
5343
5344 for (var _j4 = 0; _j4 < n; _j4++) {
5345 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5346 }
5347
5348 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5349 } // Check for convergence
5350
5351
5352 var K = 0;
5353
5354 for (var _i10 = 0; _i10 < n; _i10++) {
5355 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5356 e[iter % opts.minIterations * n + _i10] = E;
5357 K += E;
5358 }
5359
5360 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5361 var _sum = 0;
5362
5363 for (var _i11 = 0; _i11 < n; _i11++) {
5364 se[_i11] = 0;
5365
5366 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5367 se[_i11] += e[_j5 * n + _i11];
5368 }
5369
5370 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5371 _sum++;
5372 }
5373 }
5374
5375 if (_sum === n) {
5376 // then we have convergence
5377 break;
5378 }
5379 }
5380 } // Identify exemplars (cluster centers)
5381
5382
5383 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
5384
5385 var clusterIndices = assign$2(n, S, exemplarsIndices);
5386 var clusters = {};
5387
5388 for (var c = 0; c < exemplarsIndices.length; c++) {
5389 clusters[exemplarsIndices[c]] = [];
5390 }
5391
5392 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5393 var pos = id2position[nodes[_i12].id()];
5394
5395 var clusterIndex = clusterIndices[pos];
5396
5397 if (clusterIndex != null) {
5398 // the node may have not been assigned a cluster if no valid attributes were specified
5399 clusters[clusterIndex].push(nodes[_i12]);
5400 }
5401 }
5402
5403 var retClusters = new Array(exemplarsIndices.length);
5404
5405 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5406 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5407 }
5408
5409 return retClusters;
5410};
5411
5412var affinityPropagation$1 = {
5413 affinityPropagation: affinityPropagation,
5414 ap: affinityPropagation
5415};
5416
5417var hierholzerDefaults = defaults({
5418 root: undefined,
5419 directed: false
5420});
5421var elesfn$b = {
5422 hierholzer: function hierholzer(options) {
5423 if (!plainObject(options)) {
5424 var args = arguments;
5425 options = {
5426 root: args[0],
5427 directed: args[1]
5428 };
5429 }
5430
5431 var _hierholzerDefaults = hierholzerDefaults(options),
5432 root = _hierholzerDefaults.root,
5433 directed = _hierholzerDefaults.directed;
5434
5435 var eles = this;
5436 var dflag = false;
5437 var oddIn;
5438 var oddOut;
5439 var startVertex;
5440 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5441 var nodes = {};
5442 var edges = {};
5443
5444 if (directed) {
5445 eles.forEach(function (ele) {
5446 var id = ele.id();
5447
5448 if (ele.isNode()) {
5449 var ind = ele.indegree(true);
5450 var outd = ele.outdegree(true);
5451 var d1 = ind - outd;
5452 var d2 = outd - ind;
5453
5454 if (d1 == 1) {
5455 if (oddIn) dflag = true;else oddIn = id;
5456 } else if (d2 == 1) {
5457 if (oddOut) dflag = true;else oddOut = id;
5458 } else if (d2 > 1 || d1 > 1) {
5459 dflag = true;
5460 }
5461
5462 nodes[id] = [];
5463 ele.outgoers().forEach(function (e) {
5464 if (e.isEdge()) nodes[id].push(e.id());
5465 });
5466 } else {
5467 edges[id] = [undefined, ele.target().id()];
5468 }
5469 });
5470 } else {
5471 eles.forEach(function (ele) {
5472 var id = ele.id();
5473
5474 if (ele.isNode()) {
5475 var d = ele.degree(true);
5476
5477 if (d % 2) {
5478 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5479 }
5480
5481 nodes[id] = [];
5482 ele.connectedEdges().forEach(function (e) {
5483 return nodes[id].push(e.id());
5484 });
5485 } else {
5486 edges[id] = [ele.source().id(), ele.target().id()];
5487 }
5488 });
5489 }
5490
5491 var result = {
5492 found: false,
5493 trail: undefined
5494 };
5495 if (dflag) return result;else if (oddOut && oddIn) {
5496 if (directed) {
5497 if (startVertex && oddOut != startVertex) {
5498 return result;
5499 }
5500
5501 startVertex = oddOut;
5502 } else {
5503 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5504 return result;
5505 } else if (!startVertex) {
5506 startVertex = oddOut;
5507 }
5508 }
5509 } else {
5510 if (!startVertex) startVertex = eles[0].id();
5511 }
5512
5513 var walk = function walk(v) {
5514 var currentNode = v;
5515 var subtour = [v];
5516 var adj, adjTail, adjHead;
5517
5518 while (nodes[currentNode].length) {
5519 adj = nodes[currentNode].shift();
5520 adjTail = edges[adj][0];
5521 adjHead = edges[adj][1];
5522
5523 if (currentNode != adjHead) {
5524 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5525 return e != adj;
5526 });
5527 currentNode = adjHead;
5528 } else if (!directed && currentNode != adjTail) {
5529 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5530 return e != adj;
5531 });
5532 currentNode = adjTail;
5533 }
5534
5535 subtour.unshift(adj);
5536 subtour.unshift(currentNode);
5537 }
5538
5539 return subtour;
5540 };
5541
5542 var trail = [];
5543 var subtour = [];
5544 subtour = walk(startVertex);
5545
5546 while (subtour.length != 1) {
5547 if (nodes[subtour[0]].length == 0) {
5548 trail.unshift(eles.getElementById(subtour.shift()));
5549 trail.unshift(eles.getElementById(subtour.shift()));
5550 } else {
5551 subtour = walk(subtour.shift()).concat(subtour);
5552 }
5553 }
5554
5555 trail.unshift(eles.getElementById(subtour.shift())); // final node
5556
5557 for (var d in nodes) {
5558 if (nodes[d].length) {
5559 return result;
5560 }
5561 }
5562
5563 result.found = true;
5564 result.trail = this.spawn(trail);
5565 return result;
5566 }
5567};
5568
5569var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5570 var eles = this;
5571 var nodes = {};
5572 var id = 0;
5573 var edgeCount = 0;
5574 var components = [];
5575 var stack = [];
5576 var visitedEdges = {};
5577
5578 var buildComponent = function buildComponent(x, y) {
5579 var i = stack.length - 1;
5580 var cutset = [];
5581 var component = eles.spawn();
5582
5583 while (stack[i].x != x || stack[i].y != y) {
5584 cutset.push(stack.pop().edge);
5585 i--;
5586 }
5587
5588 cutset.push(stack.pop().edge);
5589 cutset.forEach(function (edge) {
5590 var connectedNodes = edge.connectedNodes().intersection(eles);
5591 component.merge(edge);
5592 connectedNodes.forEach(function (node) {
5593 var nodeId = node.id();
5594 var connectedEdges = node.connectedEdges().intersection(eles);
5595 component.merge(node);
5596
5597 if (!nodes[nodeId].cutVertex) {
5598 component.merge(connectedEdges);
5599 } else {
5600 component.merge(connectedEdges.filter(function (edge) {
5601 return edge.isLoop();
5602 }));
5603 }
5604 });
5605 });
5606 components.push(component);
5607 };
5608
5609 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5610 if (root === parent) edgeCount += 1;
5611 nodes[currentNode] = {
5612 id: id,
5613 low: id++,
5614 cutVertex: false
5615 };
5616 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5617
5618 if (edges.size() === 0) {
5619 components.push(eles.spawn(eles.getElementById(currentNode)));
5620 } else {
5621 var sourceId, targetId, otherNodeId, edgeId;
5622 edges.forEach(function (edge) {
5623 sourceId = edge.source().id();
5624 targetId = edge.target().id();
5625 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5626
5627 if (otherNodeId !== parent) {
5628 edgeId = edge.id();
5629
5630 if (!visitedEdges[edgeId]) {
5631 visitedEdges[edgeId] = true;
5632 stack.push({
5633 x: currentNode,
5634 y: otherNodeId,
5635 edge: edge
5636 });
5637 }
5638
5639 if (!(otherNodeId in nodes)) {
5640 biconnectedSearch(root, otherNodeId, currentNode);
5641 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
5642
5643 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
5644 nodes[currentNode].cutVertex = true;
5645 buildComponent(currentNode, otherNodeId);
5646 }
5647 } else {
5648 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
5649 }
5650 }
5651 });
5652 }
5653 };
5654
5655 eles.forEach(function (ele) {
5656 if (ele.isNode()) {
5657 var nodeId = ele.id();
5658
5659 if (!(nodeId in nodes)) {
5660 edgeCount = 0;
5661 biconnectedSearch(nodeId, nodeId);
5662 nodes[nodeId].cutVertex = edgeCount > 1;
5663 }
5664 }
5665 });
5666 var cutVertices = Object.keys(nodes).filter(function (id) {
5667 return nodes[id].cutVertex;
5668 }).map(function (id) {
5669 return eles.getElementById(id);
5670 });
5671 return {
5672 cut: eles.spawn(cutVertices),
5673 components: components
5674 };
5675};
5676
5677var hopcroftTarjanBiconnected$1 = {
5678 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
5679 htbc: hopcroftTarjanBiconnected,
5680 htb: hopcroftTarjanBiconnected,
5681 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
5682};
5683
5684var tarjanStronglyConnected = function tarjanStronglyConnected() {
5685 var eles = this;
5686 var nodes = {};
5687 var index = 0;
5688 var components = [];
5689 var stack = [];
5690 var cut = eles.spawn(eles);
5691
5692 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
5693 stack.push(sourceNodeId);
5694 nodes[sourceNodeId] = {
5695 index: index,
5696 low: index++,
5697 explored: false
5698 };
5699 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
5700 connectedEdges.forEach(function (edge) {
5701 var targetNodeId = edge.target().id();
5702
5703 if (targetNodeId !== sourceNodeId) {
5704 if (!(targetNodeId in nodes)) {
5705 stronglyConnectedSearch(targetNodeId);
5706 }
5707
5708 if (!nodes[targetNodeId].explored) {
5709 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
5710 }
5711 }
5712 });
5713
5714 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
5715 var componentNodes = eles.spawn();
5716
5717 for (;;) {
5718 var nodeId = stack.pop();
5719 componentNodes.merge(eles.getElementById(nodeId));
5720 nodes[nodeId].low = nodes[sourceNodeId].index;
5721 nodes[nodeId].explored = true;
5722
5723 if (nodeId === sourceNodeId) {
5724 break;
5725 }
5726 }
5727
5728 var componentEdges = componentNodes.edgesWith(componentNodes);
5729 var component = componentNodes.merge(componentEdges);
5730 components.push(component);
5731 cut = cut.difference(component);
5732 }
5733 };
5734
5735 eles.forEach(function (ele) {
5736 if (ele.isNode()) {
5737 var nodeId = ele.id();
5738
5739 if (!(nodeId in nodes)) {
5740 stronglyConnectedSearch(nodeId);
5741 }
5742 }
5743 });
5744 return {
5745 cut: cut,
5746 components: components
5747 };
5748};
5749
5750var tarjanStronglyConnected$1 = {
5751 tarjanStronglyConnected: tarjanStronglyConnected,
5752 tsc: tarjanStronglyConnected,
5753 tscc: tarjanStronglyConnected,
5754 tarjanStronglyConnectedComponents: tarjanStronglyConnected
5755};
5756
5757var elesfn$c = {};
5758[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) {
5759 extend(elesfn$c, props);
5760});
5761
5762/*!
5763Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
5764Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
5765Licensed under The MIT License (http://opensource.org/licenses/MIT)
5766*/
5767
5768/* promise states [Promises/A+ 2.1] */
5769var STATE_PENDING = 0;
5770/* [Promises/A+ 2.1.1] */
5771
5772var STATE_FULFILLED = 1;
5773/* [Promises/A+ 2.1.2] */
5774
5775var STATE_REJECTED = 2;
5776/* [Promises/A+ 2.1.3] */
5777
5778/* promise object constructor */
5779
5780var api = function api(executor) {
5781 /* optionally support non-constructor/plain-function call */
5782 if (!(this instanceof api)) return new api(executor);
5783 /* initialize object */
5784
5785 this.id = 'Thenable/1.0.7';
5786 this.state = STATE_PENDING;
5787 /* initial state */
5788
5789 this.fulfillValue = undefined;
5790 /* initial value */
5791
5792 /* [Promises/A+ 1.3, 2.1.2.2] */
5793
5794 this.rejectReason = undefined;
5795 /* initial reason */
5796
5797 /* [Promises/A+ 1.5, 2.1.3.2] */
5798
5799 this.onFulfilled = [];
5800 /* initial handlers */
5801
5802 this.onRejected = [];
5803 /* initial handlers */
5804
5805 /* provide optional information-hiding proxy */
5806
5807 this.proxy = {
5808 then: this.then.bind(this)
5809 };
5810 /* support optional executor function */
5811
5812 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
5813};
5814/* promise API methods */
5815
5816
5817api.prototype = {
5818 /* promise resolving methods */
5819 fulfill: function fulfill(value) {
5820 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
5821 },
5822 reject: function reject(value) {
5823 return deliver(this, STATE_REJECTED, 'rejectReason', value);
5824 },
5825
5826 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
5827 then: function then(onFulfilled, onRejected) {
5828 var curr = this;
5829 var next = new api();
5830 /* [Promises/A+ 2.2.7] */
5831
5832 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
5833 /* [Promises/A+ 2.2.2/2.2.6] */
5834
5835 curr.onRejected.push(resolver(onRejected, next, 'reject'));
5836 /* [Promises/A+ 2.2.3/2.2.6] */
5837
5838 execute(curr);
5839 return next.proxy;
5840 /* [Promises/A+ 2.2.7, 3.3] */
5841 }
5842};
5843/* deliver an action */
5844
5845var deliver = function deliver(curr, state, name, value) {
5846 if (curr.state === STATE_PENDING) {
5847 curr.state = state;
5848 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
5849
5850 curr[name] = value;
5851 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
5852
5853 execute(curr);
5854 }
5855
5856 return curr;
5857};
5858/* execute all handlers */
5859
5860
5861var execute = function execute(curr) {
5862 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
5863};
5864/* execute particular set of handlers */
5865
5866
5867var execute_handlers = function execute_handlers(curr, name, value) {
5868 /* global setImmediate: true */
5869
5870 /* global setTimeout: true */
5871
5872 /* short-circuit processing */
5873 if (curr[name].length === 0) return;
5874 /* iterate over all handlers, exactly once */
5875
5876 var handlers = curr[name];
5877 curr[name] = [];
5878 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
5879
5880 var func = function func() {
5881 for (var i = 0; i < handlers.length; i++) {
5882 handlers[i](value);
5883 }
5884 /* [Promises/A+ 2.2.5] */
5885
5886 };
5887 /* execute procedure asynchronously */
5888
5889 /* [Promises/A+ 2.2.4, 3.1] */
5890
5891
5892 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
5893};
5894/* generate a resolver function */
5895
5896
5897var resolver = function resolver(cb, next, method) {
5898 return function (value) {
5899 if (typeof cb !== 'function')
5900 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
5901 next[method].call(next, value);
5902 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
5903 else {
5904 var result;
5905
5906 try {
5907 result = cb(value);
5908 }
5909 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
5910 catch (e) {
5911 next.reject(e);
5912 /* [Promises/A+ 2.2.7.2] */
5913
5914 return;
5915 }
5916
5917 resolve(next, result);
5918 /* [Promises/A+ 2.2.7.1] */
5919 }
5920 };
5921};
5922/* "Promise Resolution Procedure" */
5923
5924/* [Promises/A+ 2.3] */
5925
5926
5927var resolve = function resolve(promise, x) {
5928 /* sanity check arguments */
5929
5930 /* [Promises/A+ 2.3.1] */
5931 if (promise === x || promise.proxy === x) {
5932 promise.reject(new TypeError('cannot resolve promise with itself'));
5933 return;
5934 }
5935 /* surgically check for a "then" method
5936 (mainly to just call the "getter" of "then" only once) */
5937
5938
5939 var then;
5940
5941 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
5942 try {
5943 then = x.then;
5944 }
5945 /* [Promises/A+ 2.3.3.1, 3.5] */
5946 catch (e) {
5947 promise.reject(e);
5948 /* [Promises/A+ 2.3.3.2] */
5949
5950 return;
5951 }
5952 }
5953 /* handle own Thenables [Promises/A+ 2.3.2]
5954 and similar "thenables" [Promises/A+ 2.3.3] */
5955
5956
5957 if (typeof then === 'function') {
5958 var resolved = false;
5959
5960 try {
5961 /* call retrieved "then" method */
5962
5963 /* [Promises/A+ 2.3.3.3] */
5964 then.call(x,
5965 /* resolvePromise */
5966
5967 /* [Promises/A+ 2.3.3.3.1] */
5968 function (y) {
5969 if (resolved) return;
5970 resolved = true;
5971 /* [Promises/A+ 2.3.3.3.3] */
5972
5973 if (y === x)
5974 /* [Promises/A+ 3.6] */
5975 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
5976 },
5977 /* rejectPromise */
5978
5979 /* [Promises/A+ 2.3.3.3.2] */
5980 function (r) {
5981 if (resolved) return;
5982 resolved = true;
5983 /* [Promises/A+ 2.3.3.3.3] */
5984
5985 promise.reject(r);
5986 });
5987 } catch (e) {
5988 if (!resolved)
5989 /* [Promises/A+ 2.3.3.3.3] */
5990 promise.reject(e);
5991 /* [Promises/A+ 2.3.3.3.4] */
5992 }
5993
5994 return;
5995 }
5996 /* handle other values */
5997
5998
5999 promise.fulfill(x);
6000 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6001}; // so we always have Promise.all()
6002
6003
6004api.all = function (ps) {
6005 return new api(function (resolveAll, rejectAll) {
6006 var vals = new Array(ps.length);
6007 var doneCount = 0;
6008
6009 var fulfill = function fulfill(i, val) {
6010 vals[i] = val;
6011 doneCount++;
6012
6013 if (doneCount === ps.length) {
6014 resolveAll(vals);
6015 }
6016 };
6017
6018 for (var i = 0; i < ps.length; i++) {
6019 (function (i) {
6020 var p = ps[i];
6021 var isPromise = p != null && p.then != null;
6022
6023 if (isPromise) {
6024 p.then(function (val) {
6025 fulfill(i, val);
6026 }, function (err) {
6027 rejectAll(err);
6028 });
6029 } else {
6030 var val = p;
6031 fulfill(i, val);
6032 }
6033 })(i);
6034 }
6035 });
6036};
6037
6038api.resolve = function (val) {
6039 return new api(function (resolve, reject) {
6040 resolve(val);
6041 });
6042};
6043
6044api.reject = function (val) {
6045 return new api(function (resolve, reject) {
6046 reject(val);
6047 });
6048};
6049
6050var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6051
6052var Animation = function Animation(target, opts, opts2) {
6053 var isCore = core(target);
6054 var isEle = !isCore;
6055
6056 var _p = this._private = extend({
6057 duration: 1000
6058 }, opts, opts2);
6059
6060 _p.target = target;
6061 _p.style = _p.style || _p.css;
6062 _p.started = false;
6063 _p.playing = false;
6064 _p.hooked = false;
6065 _p.applying = false;
6066 _p.progress = 0;
6067 _p.completes = [];
6068 _p.frames = [];
6069
6070 if (_p.complete && fn(_p.complete)) {
6071 _p.completes.push(_p.complete);
6072 }
6073
6074 if (isEle) {
6075 var pos = target.position();
6076 _p.startPosition = _p.startPosition || {
6077 x: pos.x,
6078 y: pos.y
6079 };
6080 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6081 }
6082
6083 if (isCore) {
6084 var pan = target.pan();
6085 _p.startPan = {
6086 x: pan.x,
6087 y: pan.y
6088 };
6089 _p.startZoom = target.zoom();
6090 } // for future timeline/animations impl
6091
6092
6093 this.length = 1;
6094 this[0] = this;
6095};
6096
6097var anifn = Animation.prototype;
6098extend(anifn, {
6099 instanceString: function instanceString() {
6100 return 'animation';
6101 },
6102 hook: function hook() {
6103 var _p = this._private;
6104
6105 if (!_p.hooked) {
6106 // add to target's animation queue
6107 var q;
6108 var tAni = _p.target._private.animation;
6109
6110 if (_p.queue) {
6111 q = tAni.queue;
6112 } else {
6113 q = tAni.current;
6114 }
6115
6116 q.push(this); // add to the animation loop pool
6117
6118 if (elementOrCollection(_p.target)) {
6119 _p.target.cy().addToAnimationPool(_p.target);
6120 }
6121
6122 _p.hooked = true;
6123 }
6124
6125 return this;
6126 },
6127 play: function play() {
6128 var _p = this._private; // autorewind
6129
6130 if (_p.progress === 1) {
6131 _p.progress = 0;
6132 }
6133
6134 _p.playing = true;
6135 _p.started = false; // needs to be started by animation loop
6136
6137 _p.stopped = false;
6138 this.hook(); // the animation loop will start the animation...
6139
6140 return this;
6141 },
6142 playing: function playing() {
6143 return this._private.playing;
6144 },
6145 apply: function apply() {
6146 var _p = this._private;
6147 _p.applying = true;
6148 _p.started = false; // needs to be started by animation loop
6149
6150 _p.stopped = false;
6151 this.hook(); // the animation loop will apply the animation at this progress
6152
6153 return this;
6154 },
6155 applying: function applying() {
6156 return this._private.applying;
6157 },
6158 pause: function pause() {
6159 var _p = this._private;
6160 _p.playing = false;
6161 _p.started = false;
6162 return this;
6163 },
6164 stop: function stop() {
6165 var _p = this._private;
6166 _p.playing = false;
6167 _p.started = false;
6168 _p.stopped = true; // to be removed from animation queues
6169
6170 return this;
6171 },
6172 rewind: function rewind() {
6173 return this.progress(0);
6174 },
6175 fastforward: function fastforward() {
6176 return this.progress(1);
6177 },
6178 time: function time(t) {
6179 var _p = this._private;
6180
6181 if (t === undefined) {
6182 return _p.progress * _p.duration;
6183 } else {
6184 return this.progress(t / _p.duration);
6185 }
6186 },
6187 progress: function progress(p) {
6188 var _p = this._private;
6189 var wasPlaying = _p.playing;
6190
6191 if (p === undefined) {
6192 return _p.progress;
6193 } else {
6194 if (wasPlaying) {
6195 this.pause();
6196 }
6197
6198 _p.progress = p;
6199 _p.started = false;
6200
6201 if (wasPlaying) {
6202 this.play();
6203 }
6204 }
6205
6206 return this;
6207 },
6208 completed: function completed() {
6209 return this._private.progress === 1;
6210 },
6211 reverse: function reverse() {
6212 var _p = this._private;
6213 var wasPlaying = _p.playing;
6214
6215 if (wasPlaying) {
6216 this.pause();
6217 }
6218
6219 _p.progress = 1 - _p.progress;
6220 _p.started = false;
6221
6222 var swap = function swap(a, b) {
6223 var _pa = _p[a];
6224
6225 if (_pa == null) {
6226 return;
6227 }
6228
6229 _p[a] = _p[b];
6230 _p[b] = _pa;
6231 };
6232
6233 swap('zoom', 'startZoom');
6234 swap('pan', 'startPan');
6235 swap('position', 'startPosition'); // swap styles
6236
6237 if (_p.style) {
6238 for (var i = 0; i < _p.style.length; i++) {
6239 var prop = _p.style[i];
6240 var name = prop.name;
6241 var startStyleProp = _p.startStyle[name];
6242 _p.startStyle[name] = prop;
6243 _p.style[i] = startStyleProp;
6244 }
6245 }
6246
6247 if (wasPlaying) {
6248 this.play();
6249 }
6250
6251 return this;
6252 },
6253 promise: function promise(type) {
6254 var _p = this._private;
6255 var arr;
6256
6257 switch (type) {
6258 case 'frame':
6259 arr = _p.frames;
6260 break;
6261
6262 default:
6263 case 'complete':
6264 case 'completed':
6265 arr = _p.completes;
6266 }
6267
6268 return new Promise$1(function (resolve, reject) {
6269 arr.push(function () {
6270 resolve();
6271 });
6272 });
6273 }
6274});
6275anifn.complete = anifn.completed;
6276anifn.run = anifn.play;
6277anifn.running = anifn.playing;
6278
6279var define = {
6280 animated: function animated() {
6281 return function animatedImpl() {
6282 var self = this;
6283 var selfIsArrayLike = self.length !== undefined;
6284 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6285
6286 var cy = this._private.cy || this;
6287
6288 if (!cy.styleEnabled()) {
6289 return false;
6290 }
6291
6292 var ele = all[0];
6293
6294 if (ele) {
6295 return ele._private.animation.current.length > 0;
6296 }
6297 };
6298 },
6299 // animated
6300 clearQueue: function clearQueue() {
6301 return function clearQueueImpl() {
6302 var self = this;
6303 var selfIsArrayLike = self.length !== undefined;
6304 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6305
6306 var cy = this._private.cy || this;
6307
6308 if (!cy.styleEnabled()) {
6309 return this;
6310 }
6311
6312 for (var i = 0; i < all.length; i++) {
6313 var ele = all[i];
6314 ele._private.animation.queue = [];
6315 }
6316
6317 return this;
6318 };
6319 },
6320 // clearQueue
6321 delay: function delay() {
6322 return function delayImpl(time, complete) {
6323 var cy = this._private.cy || this;
6324
6325 if (!cy.styleEnabled()) {
6326 return this;
6327 }
6328
6329 return this.animate({
6330 delay: time,
6331 duration: time,
6332 complete: complete
6333 });
6334 };
6335 },
6336 // delay
6337 delayAnimation: function delayAnimation() {
6338 return function delayAnimationImpl(time, complete) {
6339 var cy = this._private.cy || this;
6340
6341 if (!cy.styleEnabled()) {
6342 return this;
6343 }
6344
6345 return this.animation({
6346 delay: time,
6347 duration: time,
6348 complete: complete
6349 });
6350 };
6351 },
6352 // delay
6353 animation: function animation() {
6354 return function animationImpl(properties, params) {
6355 var self = this;
6356 var selfIsArrayLike = self.length !== undefined;
6357 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6358
6359 var cy = this._private.cy || this;
6360 var isCore = !selfIsArrayLike;
6361 var isEles = !isCore;
6362
6363 if (!cy.styleEnabled()) {
6364 return this;
6365 }
6366
6367 var style = cy.style();
6368 properties = extend({}, properties, params);
6369 var propertiesEmpty = Object.keys(properties).length === 0;
6370
6371 if (propertiesEmpty) {
6372 return new Animation(all[0], properties); // nothing to animate
6373 }
6374
6375 if (properties.duration === undefined) {
6376 properties.duration = 400;
6377 }
6378
6379 switch (properties.duration) {
6380 case 'slow':
6381 properties.duration = 600;
6382 break;
6383
6384 case 'fast':
6385 properties.duration = 200;
6386 break;
6387 }
6388
6389 if (isEles) {
6390 properties.style = style.getPropsList(properties.style || properties.css);
6391 properties.css = undefined;
6392 }
6393
6394 if (isEles && properties.renderedPosition != null) {
6395 var rpos = properties.renderedPosition;
6396 var pan = cy.pan();
6397 var zoom = cy.zoom();
6398 properties.position = renderedToModelPosition(rpos, zoom, pan);
6399 } // override pan w/ panBy if set
6400
6401
6402 if (isCore && properties.panBy != null) {
6403 var panBy = properties.panBy;
6404 var cyPan = cy.pan();
6405 properties.pan = {
6406 x: cyPan.x + panBy.x,
6407 y: cyPan.y + panBy.y
6408 };
6409 } // override pan w/ center if set
6410
6411
6412 var center = properties.center || properties.centre;
6413
6414 if (isCore && center != null) {
6415 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6416
6417 if (centerPan != null) {
6418 properties.pan = centerPan;
6419 }
6420 } // override pan & zoom w/ fit if set
6421
6422
6423 if (isCore && properties.fit != null) {
6424 var fit = properties.fit;
6425 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6426
6427 if (fitVp != null) {
6428 properties.pan = fitVp.pan;
6429 properties.zoom = fitVp.zoom;
6430 }
6431 } // override zoom (& potentially pan) w/ zoom obj if set
6432
6433
6434 if (isCore && plainObject(properties.zoom)) {
6435 var vp = cy.getZoomedViewport(properties.zoom);
6436
6437 if (vp != null) {
6438 if (vp.zoomed) {
6439 properties.zoom = vp.zoom;
6440 }
6441
6442 if (vp.panned) {
6443 properties.pan = vp.pan;
6444 }
6445 } else {
6446 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6447 }
6448 }
6449
6450 return new Animation(all[0], properties);
6451 };
6452 },
6453 // animate
6454 animate: function animate() {
6455 return function animateImpl(properties, params) {
6456 var self = this;
6457 var selfIsArrayLike = self.length !== undefined;
6458 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6459
6460 var cy = this._private.cy || this;
6461
6462 if (!cy.styleEnabled()) {
6463 return this;
6464 }
6465
6466 if (params) {
6467 properties = extend({}, properties, params);
6468 } // manually hook and run the animation
6469
6470
6471 for (var i = 0; i < all.length; i++) {
6472 var ele = all[i];
6473 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6474 var ani = ele.animation(properties, queue ? {
6475 queue: true
6476 } : undefined);
6477 ani.play();
6478 }
6479
6480 return this; // chaining
6481 };
6482 },
6483 // animate
6484 stop: function stop() {
6485 return function stopImpl(clearQueue, jumpToEnd) {
6486 var self = this;
6487 var selfIsArrayLike = self.length !== undefined;
6488 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6489
6490 var cy = this._private.cy || this;
6491
6492 if (!cy.styleEnabled()) {
6493 return this;
6494 }
6495
6496 for (var i = 0; i < all.length; i++) {
6497 var ele = all[i];
6498 var _p = ele._private;
6499 var anis = _p.animation.current;
6500
6501 for (var j = 0; j < anis.length; j++) {
6502 var ani = anis[j];
6503 var ani_p = ani._private;
6504
6505 if (jumpToEnd) {
6506 // next iteration of the animation loop, the animation
6507 // will go straight to the end and be removed
6508 ani_p.duration = 0;
6509 }
6510 } // clear the queue of future animations
6511
6512
6513 if (clearQueue) {
6514 _p.animation.queue = [];
6515 }
6516
6517 if (!jumpToEnd) {
6518 _p.animation.current = [];
6519 }
6520 } // we have to notify (the animation loop doesn't do it for us on `stop`)
6521
6522
6523 cy.notify('draw');
6524 return this;
6525 };
6526 } // stop
6527
6528}; // define
6529
6530var define$1 = {
6531 // access data field
6532 data: function data(params) {
6533 var defaults = {
6534 field: 'data',
6535 bindingEvent: 'data',
6536 allowBinding: false,
6537 allowSetting: false,
6538 allowGetting: false,
6539 settingEvent: 'data',
6540 settingTriggersEvent: false,
6541 triggerFnName: 'trigger',
6542 immutableKeys: {},
6543 // key => true if immutable
6544 updateStyle: false,
6545 beforeGet: function beforeGet(self) {},
6546 beforeSet: function beforeSet(self, obj) {},
6547 onSet: function onSet(self) {},
6548 canSet: function canSet(self) {
6549 return true;
6550 }
6551 };
6552 params = extend({}, defaults, params);
6553 return function dataImpl(name, value) {
6554 var p = params;
6555 var self = this;
6556 var selfIsArrayLike = self.length !== undefined;
6557 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6558
6559 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
6560
6561 if (string(name)) {
6562 // set or get property
6563 // .data('foo')
6564 if (p.allowGetting && value === undefined) {
6565 // get
6566 var ret;
6567
6568 if (single) {
6569 p.beforeGet(single);
6570 ret = single._private[p.field][name];
6571 }
6572
6573 return ret; // .data('foo', 'bar')
6574 } else if (p.allowSetting && value !== undefined) {
6575 // set
6576 var valid = !p.immutableKeys[name];
6577
6578 if (valid) {
6579 var change = _defineProperty({}, name, value);
6580
6581 p.beforeSet(self, change);
6582
6583 for (var i = 0, l = all.length; i < l; i++) {
6584 var ele = all[i];
6585
6586 if (p.canSet(ele)) {
6587 ele._private[p.field][name] = value;
6588 }
6589 } // update mappers if asked
6590
6591
6592 if (p.updateStyle) {
6593 self.updateStyle();
6594 } // call onSet callback
6595
6596
6597 p.onSet(self);
6598
6599 if (p.settingTriggersEvent) {
6600 self[p.triggerFnName](p.settingEvent);
6601 }
6602 }
6603 } // .data({ 'foo': 'bar' })
6604
6605 } else if (p.allowSetting && plainObject(name)) {
6606 // extend
6607 var obj = name;
6608 var k, v;
6609 var keys = Object.keys(obj);
6610 p.beforeSet(self, obj);
6611
6612 for (var _i = 0; _i < keys.length; _i++) {
6613 k = keys[_i];
6614 v = obj[k];
6615
6616 var _valid = !p.immutableKeys[k];
6617
6618 if (_valid) {
6619 for (var j = 0; j < all.length; j++) {
6620 var _ele = all[j];
6621
6622 if (p.canSet(_ele)) {
6623 _ele._private[p.field][k] = v;
6624 }
6625 }
6626 }
6627 } // update mappers if asked
6628
6629
6630 if (p.updateStyle) {
6631 self.updateStyle();
6632 } // call onSet callback
6633
6634
6635 p.onSet(self);
6636
6637 if (p.settingTriggersEvent) {
6638 self[p.triggerFnName](p.settingEvent);
6639 } // .data(function(){ ... })
6640
6641 } else if (p.allowBinding && fn(name)) {
6642 // bind to event
6643 var fn$1 = name;
6644 self.on(p.bindingEvent, fn$1); // .data()
6645 } else if (p.allowGetting && name === undefined) {
6646 // get whole object
6647 var _ret;
6648
6649 if (single) {
6650 p.beforeGet(single);
6651 _ret = single._private[p.field];
6652 }
6653
6654 return _ret;
6655 }
6656
6657 return self; // maintain chainability
6658 }; // function
6659 },
6660 // data
6661 // remove data field
6662 removeData: function removeData(params) {
6663 var defaults = {
6664 field: 'data',
6665 event: 'data',
6666 triggerFnName: 'trigger',
6667 triggerEvent: false,
6668 immutableKeys: {} // key => true if immutable
6669
6670 };
6671 params = extend({}, defaults, params);
6672 return function removeDataImpl(names) {
6673 var p = params;
6674 var self = this;
6675 var selfIsArrayLike = self.length !== undefined;
6676 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6677 // .removeData('foo bar')
6678
6679 if (string(names)) {
6680 // then get the list of keys, and delete them
6681 var keys = names.split(/\s+/);
6682 var l = keys.length;
6683
6684 for (var i = 0; i < l; i++) {
6685 // delete each non-empty key
6686 var key = keys[i];
6687
6688 if (emptyString(key)) {
6689 continue;
6690 }
6691
6692 var valid = !p.immutableKeys[key]; // not valid if immutable
6693
6694 if (valid) {
6695 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
6696 all[i_a]._private[p.field][key] = undefined;
6697 }
6698 }
6699 }
6700
6701 if (p.triggerEvent) {
6702 self[p.triggerFnName](p.event);
6703 } // .removeData()
6704
6705 } else if (names === undefined) {
6706 // then delete all keys
6707 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
6708 var _privateFields = all[_i_a]._private[p.field];
6709
6710 var _keys = Object.keys(_privateFields);
6711
6712 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
6713 var _key = _keys[_i2];
6714 var validKeyToDelete = !p.immutableKeys[_key];
6715
6716 if (validKeyToDelete) {
6717 _privateFields[_key] = undefined;
6718 }
6719 }
6720 }
6721
6722 if (p.triggerEvent) {
6723 self[p.triggerFnName](p.event);
6724 }
6725 }
6726
6727 return self; // maintain chaining
6728 }; // function
6729 } // removeData
6730
6731}; // define
6732
6733var define$2 = {
6734 eventAliasesOn: function eventAliasesOn(proto) {
6735 var p = proto;
6736 p.addListener = p.listen = p.bind = p.on;
6737 p.unlisten = p.unbind = p.off = p.removeListener;
6738 p.trigger = p.emit; // this is just a wrapper alias of .on()
6739
6740 p.pon = p.promiseOn = function (events, selector) {
6741 var self = this;
6742 var args = Array.prototype.slice.call(arguments, 0);
6743 return new Promise$1(function (resolve, reject) {
6744 var callback = function callback(e) {
6745 self.off.apply(self, offArgs);
6746 resolve(e);
6747 };
6748
6749 var onArgs = args.concat([callback]);
6750 var offArgs = onArgs.concat([]);
6751 self.on.apply(self, onArgs);
6752 });
6753 };
6754 }
6755}; // define
6756
6757// use this module to cherry pick functions into your prototype
6758var define$3 = {};
6759[define, define$1, define$2].forEach(function (m) {
6760 extend(define$3, m);
6761});
6762
6763var elesfn$d = {
6764 animate: define$3.animate(),
6765 animation: define$3.animation(),
6766 animated: define$3.animated(),
6767 clearQueue: define$3.clearQueue(),
6768 delay: define$3.delay(),
6769 delayAnimation: define$3.delayAnimation(),
6770 stop: define$3.stop()
6771};
6772
6773var elesfn$e = {
6774 classes: function classes(_classes) {
6775 var self = this;
6776
6777 if (_classes === undefined) {
6778 var ret = [];
6779
6780 self[0]._private.classes.forEach(function (cls) {
6781 return ret.push(cls);
6782 });
6783
6784 return ret;
6785 } else if (!array(_classes)) {
6786 // extract classes from string
6787 _classes = (_classes || '').match(/\S+/g) || [];
6788 }
6789
6790 var changed = [];
6791 var classesSet = new Set$1(_classes); // check and update each ele
6792
6793 for (var j = 0; j < self.length; j++) {
6794 var ele = self[j];
6795 var _p = ele._private;
6796 var eleClasses = _p.classes;
6797 var changedEle = false; // check if ele has all of the passed classes
6798
6799 for (var i = 0; i < _classes.length; i++) {
6800 var cls = _classes[i];
6801 var eleHasClass = eleClasses.has(cls);
6802
6803 if (!eleHasClass) {
6804 changedEle = true;
6805 break;
6806 }
6807 } // check if ele has classes outside of those passed
6808
6809
6810 if (!changedEle) {
6811 changedEle = eleClasses.size !== _classes.length;
6812 }
6813
6814 if (changedEle) {
6815 _p.classes = classesSet;
6816 changed.push(ele);
6817 }
6818 } // trigger update style on those eles that had class changes
6819
6820
6821 if (changed.length > 0) {
6822 this.spawn(changed).updateStyle().emit('class');
6823 }
6824
6825 return self;
6826 },
6827 addClass: function addClass(classes) {
6828 return this.toggleClass(classes, true);
6829 },
6830 hasClass: function hasClass(className) {
6831 var ele = this[0];
6832 return ele != null && ele._private.classes.has(className);
6833 },
6834 toggleClass: function toggleClass(classes, toggle) {
6835 if (!array(classes)) {
6836 // extract classes from string
6837 classes = classes.match(/\S+/g) || [];
6838 }
6839
6840 var self = this;
6841 var toggleUndefd = toggle === undefined;
6842 var changed = []; // eles who had classes changed
6843
6844 for (var i = 0, il = self.length; i < il; i++) {
6845 var ele = self[i];
6846 var eleClasses = ele._private.classes;
6847 var changedEle = false;
6848
6849 for (var j = 0; j < classes.length; j++) {
6850 var cls = classes[j];
6851 var hasClass = eleClasses.has(cls);
6852 var changedNow = false;
6853
6854 if (toggle || toggleUndefd && !hasClass) {
6855 eleClasses.add(cls);
6856 changedNow = true;
6857 } else if (!toggle || toggleUndefd && hasClass) {
6858 eleClasses["delete"](cls);
6859 changedNow = true;
6860 }
6861
6862 if (!changedEle && changedNow) {
6863 changed.push(ele);
6864 changedEle = true;
6865 }
6866 } // for j classes
6867
6868 } // for i eles
6869 // trigger update style on those eles that had class changes
6870
6871
6872 if (changed.length > 0) {
6873 this.spawn(changed).updateStyle().emit('class');
6874 }
6875
6876 return self;
6877 },
6878 removeClass: function removeClass(classes) {
6879 return this.toggleClass(classes, false);
6880 },
6881 flashClass: function flashClass(classes, duration) {
6882 var self = this;
6883
6884 if (duration == null) {
6885 duration = 250;
6886 } else if (duration === 0) {
6887 return self; // nothing to do really
6888 }
6889
6890 self.addClass(classes);
6891 setTimeout(function () {
6892 self.removeClass(classes);
6893 }, duration);
6894 return self;
6895 }
6896};
6897elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
6898
6899var tokens = {
6900 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
6901 // chars we need to escape in let names, etc
6902 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
6903 // binary comparison op (used in data selectors)
6904 boolOp: '\\?|\\!|\\^',
6905 // boolean (unary) operators (used in data selectors)
6906 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
6907 // string literals (used in data selectors) -- doublequotes | singlequotes
6908 number: number$1,
6909 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
6910 meta: 'degree|indegree|outdegree',
6911 // allowed metadata fields (i.e. allowed functions to use from Collection)
6912 separator: '\\s*,\\s*',
6913 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
6914 descendant: '\\s+',
6915 child: '\\s+>\\s+',
6916 subject: '\\$',
6917 group: 'node|edge|\\*',
6918 directedEdge: '\\s+->\\s+',
6919 undirectedEdge: '\\s+<->\\s+'
6920};
6921tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
6922
6923tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
6924
6925tokens.className = tokens.variable; // a class name (follows variable conventions)
6926
6927tokens.id = tokens.variable; // an element id (follows variable conventions)
6928
6929(function () {
6930 var ops, op, i; // add @ variants to comparatorOp
6931
6932 ops = tokens.comparatorOp.split('|');
6933
6934 for (i = 0; i < ops.length; i++) {
6935 op = ops[i];
6936 tokens.comparatorOp += '|@' + op;
6937 } // add ! variants to comparatorOp
6938
6939
6940 ops = tokens.comparatorOp.split('|');
6941
6942 for (i = 0; i < ops.length; i++) {
6943 op = ops[i];
6944
6945 if (op.indexOf('!') >= 0) {
6946 continue;
6947 } // skip ops that explicitly contain !
6948
6949
6950 if (op === '=') {
6951 continue;
6952 } // skip = b/c != is explicitly defined
6953
6954
6955 tokens.comparatorOp += '|\\!' + op;
6956 }
6957})();
6958
6959/**
6960 * Make a new query object
6961 *
6962 * @prop type {Type} The type enum (int) of the query
6963 * @prop checks List of checks to make against an ele to test for a match
6964 */
6965var newQuery = function newQuery() {
6966 return {
6967 checks: []
6968 };
6969};
6970
6971/**
6972 * A check type enum-like object. Uses integer values for fast match() lookup.
6973 * The ordering does not matter as long as the ints are unique.
6974 */
6975var Type = {
6976 /** E.g. node */
6977 GROUP: 0,
6978
6979 /** A collection of elements */
6980 COLLECTION: 1,
6981
6982 /** A filter(ele) function */
6983 FILTER: 2,
6984
6985 /** E.g. [foo > 1] */
6986 DATA_COMPARE: 3,
6987
6988 /** E.g. [foo] */
6989 DATA_EXIST: 4,
6990
6991 /** E.g. [?foo] */
6992 DATA_BOOL: 5,
6993
6994 /** E.g. [[degree > 2]] */
6995 META_COMPARE: 6,
6996
6997 /** E.g. :selected */
6998 STATE: 7,
6999
7000 /** E.g. #foo */
7001 ID: 8,
7002
7003 /** E.g. .foo */
7004 CLASS: 9,
7005
7006 /** E.g. #foo <-> #bar */
7007 UNDIRECTED_EDGE: 10,
7008
7009 /** E.g. #foo -> #bar */
7010 DIRECTED_EDGE: 11,
7011
7012 /** E.g. $#foo -> #bar */
7013 NODE_SOURCE: 12,
7014
7015 /** E.g. #foo -> $#bar */
7016 NODE_TARGET: 13,
7017
7018 /** E.g. $#foo <-> #bar */
7019 NODE_NEIGHBOR: 14,
7020
7021 /** E.g. #foo > #bar */
7022 CHILD: 15,
7023
7024 /** E.g. #foo #bar */
7025 DESCENDANT: 16,
7026
7027 /** E.g. $#foo > #bar */
7028 PARENT: 17,
7029
7030 /** E.g. $#foo #bar */
7031 ANCESTOR: 18,
7032
7033 /** E.g. #foo > $bar > #baz */
7034 COMPOUND_SPLIT: 19,
7035
7036 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7037 TRUE: 20
7038};
7039
7040var stateSelectors = [{
7041 selector: ':selected',
7042 matches: function matches(ele) {
7043 return ele.selected();
7044 }
7045}, {
7046 selector: ':unselected',
7047 matches: function matches(ele) {
7048 return !ele.selected();
7049 }
7050}, {
7051 selector: ':selectable',
7052 matches: function matches(ele) {
7053 return ele.selectable();
7054 }
7055}, {
7056 selector: ':unselectable',
7057 matches: function matches(ele) {
7058 return !ele.selectable();
7059 }
7060}, {
7061 selector: ':locked',
7062 matches: function matches(ele) {
7063 return ele.locked();
7064 }
7065}, {
7066 selector: ':unlocked',
7067 matches: function matches(ele) {
7068 return !ele.locked();
7069 }
7070}, {
7071 selector: ':visible',
7072 matches: function matches(ele) {
7073 return ele.visible();
7074 }
7075}, {
7076 selector: ':hidden',
7077 matches: function matches(ele) {
7078 return !ele.visible();
7079 }
7080}, {
7081 selector: ':transparent',
7082 matches: function matches(ele) {
7083 return ele.transparent();
7084 }
7085}, {
7086 selector: ':grabbed',
7087 matches: function matches(ele) {
7088 return ele.grabbed();
7089 }
7090}, {
7091 selector: ':free',
7092 matches: function matches(ele) {
7093 return !ele.grabbed();
7094 }
7095}, {
7096 selector: ':removed',
7097 matches: function matches(ele) {
7098 return ele.removed();
7099 }
7100}, {
7101 selector: ':inside',
7102 matches: function matches(ele) {
7103 return !ele.removed();
7104 }
7105}, {
7106 selector: ':grabbable',
7107 matches: function matches(ele) {
7108 return ele.grabbable();
7109 }
7110}, {
7111 selector: ':ungrabbable',
7112 matches: function matches(ele) {
7113 return !ele.grabbable();
7114 }
7115}, {
7116 selector: ':animated',
7117 matches: function matches(ele) {
7118 return ele.animated();
7119 }
7120}, {
7121 selector: ':unanimated',
7122 matches: function matches(ele) {
7123 return !ele.animated();
7124 }
7125}, {
7126 selector: ':parent',
7127 matches: function matches(ele) {
7128 return ele.isParent();
7129 }
7130}, {
7131 selector: ':childless',
7132 matches: function matches(ele) {
7133 return ele.isChildless();
7134 }
7135}, {
7136 selector: ':child',
7137 matches: function matches(ele) {
7138 return ele.isChild();
7139 }
7140}, {
7141 selector: ':orphan',
7142 matches: function matches(ele) {
7143 return ele.isOrphan();
7144 }
7145}, {
7146 selector: ':nonorphan',
7147 matches: function matches(ele) {
7148 return ele.isChild();
7149 }
7150}, {
7151 selector: ':compound',
7152 matches: function matches(ele) {
7153 if (ele.isNode()) {
7154 return ele.isParent();
7155 } else {
7156 return ele.source().isParent() || ele.target().isParent();
7157 }
7158 }
7159}, {
7160 selector: ':loop',
7161 matches: function matches(ele) {
7162 return ele.isLoop();
7163 }
7164}, {
7165 selector: ':simple',
7166 matches: function matches(ele) {
7167 return ele.isSimple();
7168 }
7169}, {
7170 selector: ':active',
7171 matches: function matches(ele) {
7172 return ele.active();
7173 }
7174}, {
7175 selector: ':inactive',
7176 matches: function matches(ele) {
7177 return !ele.active();
7178 }
7179}, {
7180 selector: ':backgrounding',
7181 matches: function matches(ele) {
7182 return ele.backgrounding();
7183 }
7184}, {
7185 selector: ':nonbackgrounding',
7186 matches: function matches(ele) {
7187 return !ele.backgrounding();
7188 }
7189}].sort(function (a, b) {
7190 // n.b. selectors that are starting substrings of others must have the longer ones first
7191 return descending(a.selector, b.selector);
7192});
7193
7194var lookup = function () {
7195 var selToFn = {};
7196 var s;
7197
7198 for (var i = 0; i < stateSelectors.length; i++) {
7199 s = stateSelectors[i];
7200 selToFn[s.selector] = s.matches;
7201 }
7202
7203 return selToFn;
7204}();
7205
7206var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7207 return lookup[sel](ele);
7208};
7209var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7210 return s.selector;
7211}).join('|') + ')';
7212
7213// so that values get compared properly in Selector.filter()
7214
7215var cleanMetaChars = function cleanMetaChars(str) {
7216 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7217 return $1;
7218 });
7219};
7220
7221var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7222 selector[selector.length - 1] = replacementQuery;
7223}; // NOTE: add new expression syntax here to have it recognised by the parser;
7224// - a query contains all adjacent (i.e. no separator in between) expressions;
7225// - the current query is stored in selector[i]
7226// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7227
7228
7229var exprs = [{
7230 name: 'group',
7231 // just used for identifying when debugging
7232 query: true,
7233 regex: '(' + tokens.group + ')',
7234 populate: function populate(selector, query, _ref) {
7235 var _ref2 = _slicedToArray(_ref, 1),
7236 group = _ref2[0];
7237
7238 query.checks.push({
7239 type: Type.GROUP,
7240 value: group === '*' ? group : group + 's'
7241 });
7242 }
7243}, {
7244 name: 'state',
7245 query: true,
7246 regex: stateSelectorRegex,
7247 populate: function populate(selector, query, _ref3) {
7248 var _ref4 = _slicedToArray(_ref3, 1),
7249 state = _ref4[0];
7250
7251 query.checks.push({
7252 type: Type.STATE,
7253 value: state
7254 });
7255 }
7256}, {
7257 name: 'id',
7258 query: true,
7259 regex: '\\#(' + tokens.id + ')',
7260 populate: function populate(selector, query, _ref5) {
7261 var _ref6 = _slicedToArray(_ref5, 1),
7262 id = _ref6[0];
7263
7264 query.checks.push({
7265 type: Type.ID,
7266 value: cleanMetaChars(id)
7267 });
7268 }
7269}, {
7270 name: 'className',
7271 query: true,
7272 regex: '\\.(' + tokens.className + ')',
7273 populate: function populate(selector, query, _ref7) {
7274 var _ref8 = _slicedToArray(_ref7, 1),
7275 className = _ref8[0];
7276
7277 query.checks.push({
7278 type: Type.CLASS,
7279 value: cleanMetaChars(className)
7280 });
7281 }
7282}, {
7283 name: 'dataExists',
7284 query: true,
7285 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
7286 populate: function populate(selector, query, _ref9) {
7287 var _ref10 = _slicedToArray(_ref9, 1),
7288 variable = _ref10[0];
7289
7290 query.checks.push({
7291 type: Type.DATA_EXIST,
7292 field: cleanMetaChars(variable)
7293 });
7294 }
7295}, {
7296 name: 'dataCompare',
7297 query: true,
7298 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
7299 populate: function populate(selector, query, _ref11) {
7300 var _ref12 = _slicedToArray(_ref11, 3),
7301 variable = _ref12[0],
7302 comparatorOp = _ref12[1],
7303 value = _ref12[2];
7304
7305 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
7306
7307 if (valueIsString) {
7308 value = value.substring(1, value.length - 1);
7309 } else {
7310 value = parseFloat(value);
7311 }
7312
7313 query.checks.push({
7314 type: Type.DATA_COMPARE,
7315 field: cleanMetaChars(variable),
7316 operator: comparatorOp,
7317 value: value
7318 });
7319 }
7320}, {
7321 name: 'dataBool',
7322 query: true,
7323 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
7324 populate: function populate(selector, query, _ref13) {
7325 var _ref14 = _slicedToArray(_ref13, 2),
7326 boolOp = _ref14[0],
7327 variable = _ref14[1];
7328
7329 query.checks.push({
7330 type: Type.DATA_BOOL,
7331 field: cleanMetaChars(variable),
7332 operator: boolOp
7333 });
7334 }
7335}, {
7336 name: 'metaCompare',
7337 query: true,
7338 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
7339 populate: function populate(selector, query, _ref15) {
7340 var _ref16 = _slicedToArray(_ref15, 3),
7341 meta = _ref16[0],
7342 comparatorOp = _ref16[1],
7343 number = _ref16[2];
7344
7345 query.checks.push({
7346 type: Type.META_COMPARE,
7347 field: cleanMetaChars(meta),
7348 operator: comparatorOp,
7349 value: parseFloat(number)
7350 });
7351 }
7352}, {
7353 name: 'nextQuery',
7354 separator: true,
7355 regex: tokens.separator,
7356 populate: function populate(selector, query) {
7357 var currentSubject = selector.currentSubject;
7358 var edgeCount = selector.edgeCount;
7359 var compoundCount = selector.compoundCount;
7360 var lastQ = selector[selector.length - 1];
7361
7362 if (currentSubject != null) {
7363 lastQ.subject = currentSubject;
7364 selector.currentSubject = null;
7365 }
7366
7367 lastQ.edgeCount = edgeCount;
7368 lastQ.compoundCount = compoundCount;
7369 selector.edgeCount = 0;
7370 selector.compoundCount = 0; // go on to next query
7371
7372 var nextQuery = selector[selector.length++] = newQuery();
7373 return nextQuery; // this is the new query to be filled by the following exprs
7374 }
7375}, {
7376 name: 'directedEdge',
7377 separator: true,
7378 regex: tokens.directedEdge,
7379 populate: function populate(selector, query) {
7380 if (selector.currentSubject == null) {
7381 // undirected edge
7382 var edgeQuery = newQuery();
7383 var source = query;
7384 var target = newQuery();
7385 edgeQuery.checks.push({
7386 type: Type.DIRECTED_EDGE,
7387 source: source,
7388 target: target
7389 }); // the query in the selector should be the edge rather than the source
7390
7391 replaceLastQuery(selector, query, edgeQuery);
7392 selector.edgeCount++; // we're now populating the target query with expressions that follow
7393
7394 return target;
7395 } else {
7396 // source/target
7397 var srcTgtQ = newQuery();
7398 var _source = query;
7399
7400 var _target = newQuery();
7401
7402 srcTgtQ.checks.push({
7403 type: Type.NODE_SOURCE,
7404 source: _source,
7405 target: _target
7406 }); // the query in the selector should be the neighbourhood rather than the node
7407
7408 replaceLastQuery(selector, query, srcTgtQ);
7409 selector.edgeCount++;
7410 return _target; // now populating the target with the following expressions
7411 }
7412 }
7413}, {
7414 name: 'undirectedEdge',
7415 separator: true,
7416 regex: tokens.undirectedEdge,
7417 populate: function populate(selector, query) {
7418 if (selector.currentSubject == null) {
7419 // undirected edge
7420 var edgeQuery = newQuery();
7421 var source = query;
7422 var target = newQuery();
7423 edgeQuery.checks.push({
7424 type: Type.UNDIRECTED_EDGE,
7425 nodes: [source, target]
7426 }); // the query in the selector should be the edge rather than the source
7427
7428 replaceLastQuery(selector, query, edgeQuery);
7429 selector.edgeCount++; // we're now populating the target query with expressions that follow
7430
7431 return target;
7432 } else {
7433 // neighbourhood
7434 var nhoodQ = newQuery();
7435 var node = query;
7436 var neighbor = newQuery();
7437 nhoodQ.checks.push({
7438 type: Type.NODE_NEIGHBOR,
7439 node: node,
7440 neighbor: neighbor
7441 }); // the query in the selector should be the neighbourhood rather than the node
7442
7443 replaceLastQuery(selector, query, nhoodQ);
7444 return neighbor; // now populating the neighbor with following expressions
7445 }
7446 }
7447}, {
7448 name: 'child',
7449 separator: true,
7450 regex: tokens.child,
7451 populate: function populate(selector, query) {
7452 if (selector.currentSubject == null) {
7453 // default: child query
7454 var parentChildQuery = newQuery();
7455 var child = newQuery();
7456 var parent = selector[selector.length - 1];
7457 parentChildQuery.checks.push({
7458 type: Type.CHILD,
7459 parent: parent,
7460 child: child
7461 }); // the query in the selector should be the '>' itself
7462
7463 replaceLastQuery(selector, query, parentChildQuery);
7464 selector.compoundCount++; // we're now populating the child query with expressions that follow
7465
7466 return child;
7467 } else if (selector.currentSubject === query) {
7468 // compound split query
7469 var compound = newQuery();
7470 var left = selector[selector.length - 1];
7471 var right = newQuery();
7472 var subject = newQuery();
7473
7474 var _child = newQuery();
7475
7476 var _parent = newQuery(); // set up the root compound q
7477
7478
7479 compound.checks.push({
7480 type: Type.COMPOUND_SPLIT,
7481 left: left,
7482 right: right,
7483 subject: subject
7484 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7485
7486 subject.checks = query.checks; // take the checks from the left
7487
7488 query.checks = [{
7489 type: Type.TRUE
7490 }]; // checks under left refs the subject implicitly
7491 // set up the right q
7492
7493 _parent.checks.push({
7494 type: Type.TRUE
7495 }); // parent implicitly refs the subject
7496
7497
7498 right.checks.push({
7499 type: Type.PARENT,
7500 // type is swapped on right side queries
7501 parent: _parent,
7502 child: _child // empty for now
7503
7504 });
7505 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7506
7507 selector.currentSubject = subject;
7508 selector.compoundCount++;
7509 return _child; // now populating the right side's child
7510 } else {
7511 // parent query
7512 // info for parent query
7513 var _parent2 = newQuery();
7514
7515 var _child2 = newQuery();
7516
7517 var pcQChecks = [{
7518 type: Type.PARENT,
7519 parent: _parent2,
7520 child: _child2
7521 }]; // the parent-child query takes the place of the query previously being populated
7522
7523 _parent2.checks = query.checks; // the previous query contains the checks for the parent
7524
7525 query.checks = pcQChecks; // pc query takes over
7526
7527 selector.compoundCount++;
7528 return _child2; // we're now populating the child
7529 }
7530 }
7531}, {
7532 name: 'descendant',
7533 separator: true,
7534 regex: tokens.descendant,
7535 populate: function populate(selector, query) {
7536 if (selector.currentSubject == null) {
7537 // default: descendant query
7538 var ancChQuery = newQuery();
7539 var descendant = newQuery();
7540 var ancestor = selector[selector.length - 1];
7541 ancChQuery.checks.push({
7542 type: Type.DESCENDANT,
7543 ancestor: ancestor,
7544 descendant: descendant
7545 }); // the query in the selector should be the '>' itself
7546
7547 replaceLastQuery(selector, query, ancChQuery);
7548 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
7549
7550 return descendant;
7551 } else if (selector.currentSubject === query) {
7552 // compound split query
7553 var compound = newQuery();
7554 var left = selector[selector.length - 1];
7555 var right = newQuery();
7556 var subject = newQuery();
7557
7558 var _descendant = newQuery();
7559
7560 var _ancestor = newQuery(); // set up the root compound q
7561
7562
7563 compound.checks.push({
7564 type: Type.COMPOUND_SPLIT,
7565 left: left,
7566 right: right,
7567 subject: subject
7568 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7569
7570 subject.checks = query.checks; // take the checks from the left
7571
7572 query.checks = [{
7573 type: Type.TRUE
7574 }]; // checks under left refs the subject implicitly
7575 // set up the right q
7576
7577 _ancestor.checks.push({
7578 type: Type.TRUE
7579 }); // ancestor implicitly refs the subject
7580
7581
7582 right.checks.push({
7583 type: Type.ANCESTOR,
7584 // type is swapped on right side queries
7585 ancestor: _ancestor,
7586 descendant: _descendant // empty for now
7587
7588 });
7589 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7590
7591 selector.currentSubject = subject;
7592 selector.compoundCount++;
7593 return _descendant; // now populating the right side's descendant
7594 } else {
7595 // ancestor query
7596 // info for parent query
7597 var _ancestor2 = newQuery();
7598
7599 var _descendant2 = newQuery();
7600
7601 var adQChecks = [{
7602 type: Type.ANCESTOR,
7603 ancestor: _ancestor2,
7604 descendant: _descendant2
7605 }]; // the parent-child query takes the place of the query previously being populated
7606
7607 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
7608
7609 query.checks = adQChecks; // pc query takes over
7610
7611 selector.compoundCount++;
7612 return _descendant2; // we're now populating the child
7613 }
7614 }
7615}, {
7616 name: 'subject',
7617 modifier: true,
7618 regex: tokens.subject,
7619 populate: function populate(selector, query) {
7620 if (selector.currentSubject != null && selector.currentSubject !== query) {
7621 warn('Redefinition of subject in selector `' + selector.toString() + '`');
7622 return false;
7623 }
7624
7625 selector.currentSubject = query;
7626 var topQ = selector[selector.length - 1];
7627 var topChk = topQ.checks[0];
7628 var topType = topChk == null ? null : topChk.type;
7629
7630 if (topType === Type.DIRECTED_EDGE) {
7631 // directed edge with subject on the target
7632 // change to target node check
7633 topChk.type = Type.NODE_TARGET;
7634 } else if (topType === Type.UNDIRECTED_EDGE) {
7635 // undirected edge with subject on the second node
7636 // change to neighbor check
7637 topChk.type = Type.NODE_NEIGHBOR;
7638 topChk.node = topChk.nodes[1]; // second node is subject
7639
7640 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
7641
7642 topChk.nodes = null;
7643 }
7644 }
7645}];
7646exprs.forEach(function (e) {
7647 return e.regexObj = new RegExp('^' + e.regex);
7648});
7649
7650/**
7651 * Of all the expressions, find the first match in the remaining text.
7652 * @param {string} remaining The remaining text to parse
7653 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
7654 */
7655
7656var consumeExpr = function consumeExpr(remaining) {
7657 var expr;
7658 var match;
7659 var name;
7660
7661 for (var j = 0; j < exprs.length; j++) {
7662 var e = exprs[j];
7663 var n = e.name;
7664 var m = remaining.match(e.regexObj);
7665
7666 if (m != null) {
7667 match = m;
7668 expr = e;
7669 name = n;
7670 var consumed = m[0];
7671 remaining = remaining.substring(consumed.length);
7672 break; // we've consumed one expr, so we can return now
7673 }
7674 }
7675
7676 return {
7677 expr: expr,
7678 match: match,
7679 name: name,
7680 remaining: remaining
7681 };
7682};
7683/**
7684 * Consume all the leading whitespace
7685 * @param {string} remaining The text to consume
7686 * @returns The text with the leading whitespace removed
7687 */
7688
7689
7690var consumeWhitespace = function consumeWhitespace(remaining) {
7691 var match = remaining.match(/^\s+/);
7692
7693 if (match) {
7694 var consumed = match[0];
7695 remaining = remaining.substring(consumed.length);
7696 }
7697
7698 return remaining;
7699};
7700/**
7701 * Parse the string and store the parsed representation in the Selector.
7702 * @param {string} selector The selector string
7703 * @returns `true` if the selector was successfully parsed, `false` otherwise
7704 */
7705
7706
7707var parse = function parse(selector) {
7708 var self = this;
7709 var remaining = self.inputText = selector;
7710 var currentQuery = self[0] = newQuery();
7711 self.length = 1;
7712 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
7713
7714 for (;;) {
7715 var exprInfo = consumeExpr(remaining);
7716
7717 if (exprInfo.expr == null) {
7718 warn('The selector `' + selector + '`is invalid');
7719 return false;
7720 } else {
7721 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
7722
7723 var ret = exprInfo.expr.populate(self, currentQuery, args);
7724
7725 if (ret === false) {
7726 return false; // exit if population failed
7727 } else if (ret != null) {
7728 currentQuery = ret; // change the current query to be filled if the expr specifies
7729 }
7730 }
7731
7732 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
7733
7734 if (remaining.match(/^\s*$/)) {
7735 break;
7736 }
7737 }
7738
7739 var lastQ = self[self.length - 1];
7740
7741 if (self.currentSubject != null) {
7742 lastQ.subject = self.currentSubject;
7743 }
7744
7745 lastQ.edgeCount = self.edgeCount;
7746 lastQ.compoundCount = self.compoundCount;
7747
7748 for (var i = 0; i < self.length; i++) {
7749 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
7750
7751 if (q.compoundCount > 0 && q.edgeCount > 0) {
7752 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
7753 return false;
7754 }
7755
7756 if (q.edgeCount > 1) {
7757 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
7758 return false;
7759 } else if (q.edgeCount === 1) {
7760 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.');
7761 }
7762 }
7763
7764 return true; // success
7765};
7766/**
7767 * Get the selector represented as a string. This value uses default formatting,
7768 * so things like spacing may differ from the input text passed to the constructor.
7769 * @returns {string} The selector string
7770 */
7771
7772
7773var toString = function toString() {
7774 if (this.toStringCache != null) {
7775 return this.toStringCache;
7776 }
7777
7778 var clean = function clean(obj) {
7779 if (obj == null) {
7780 return '';
7781 } else {
7782 return obj;
7783 }
7784 };
7785
7786 var cleanVal = function cleanVal(val) {
7787 if (string(val)) {
7788 return '"' + val + '"';
7789 } else {
7790 return clean(val);
7791 }
7792 };
7793
7794 var space = function space(val) {
7795 return ' ' + val + ' ';
7796 };
7797
7798 var checkToString = function checkToString(check, subject) {
7799 var type = check.type,
7800 value = check.value;
7801
7802 switch (type) {
7803 case Type.GROUP:
7804 {
7805 var group = clean(value);
7806 return group.substring(0, group.length - 1);
7807 }
7808
7809 case Type.DATA_COMPARE:
7810 {
7811 var field = check.field,
7812 operator = check.operator;
7813 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
7814 }
7815
7816 case Type.DATA_BOOL:
7817 {
7818 var _operator = check.operator,
7819 _field = check.field;
7820 return '[' + clean(_operator) + _field + ']';
7821 }
7822
7823 case Type.DATA_EXIST:
7824 {
7825 var _field2 = check.field;
7826 return '[' + _field2 + ']';
7827 }
7828
7829 case Type.META_COMPARE:
7830 {
7831 var _operator2 = check.operator,
7832 _field3 = check.field;
7833 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
7834 }
7835
7836 case Type.STATE:
7837 {
7838 return value;
7839 }
7840
7841 case Type.ID:
7842 {
7843 return '#' + value;
7844 }
7845
7846 case Type.CLASS:
7847 {
7848 return '.' + value;
7849 }
7850
7851 case Type.PARENT:
7852 case Type.CHILD:
7853 {
7854 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
7855 }
7856
7857 case Type.ANCESTOR:
7858 case Type.DESCENDANT:
7859 {
7860 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
7861 }
7862
7863 case Type.COMPOUND_SPLIT:
7864 {
7865 var lhs = queryToString(check.left, subject);
7866 var sub = queryToString(check.subject, subject);
7867 var rhs = queryToString(check.right, subject);
7868 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
7869 }
7870
7871 case Type.TRUE:
7872 {
7873 return '';
7874 }
7875 }
7876 };
7877
7878 var queryToString = function queryToString(query, subject) {
7879 return query.checks.reduce(function (str, chk, i) {
7880 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
7881 }, '');
7882 };
7883
7884 var str = '';
7885
7886 for (var i = 0; i < this.length; i++) {
7887 var query = this[i];
7888 str += queryToString(query, query.subject);
7889
7890 if (this.length > 1 && i < this.length - 1) {
7891 str += ', ';
7892 }
7893 }
7894
7895 this.toStringCache = str;
7896 return str;
7897};
7898var parse$1 = {
7899 parse: parse,
7900 toString: toString
7901};
7902
7903var valCmp = function valCmp(fieldVal, operator, value) {
7904 var matches;
7905 var isFieldStr = string(fieldVal);
7906 var isFieldNum = number(fieldVal);
7907 var isValStr = string(value);
7908 var fieldStr, valStr;
7909 var caseInsensitive = false;
7910 var notExpr = false;
7911 var isIneqCmp = false;
7912
7913 if (operator.indexOf('!') >= 0) {
7914 operator = operator.replace('!', '');
7915 notExpr = true;
7916 }
7917
7918 if (operator.indexOf('@') >= 0) {
7919 operator = operator.replace('@', '');
7920 caseInsensitive = true;
7921 }
7922
7923 if (isFieldStr || isValStr || caseInsensitive) {
7924 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
7925 valStr = '' + value;
7926 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
7927 // even if we're comparing numbers
7928
7929
7930 if (caseInsensitive) {
7931 fieldVal = fieldStr = fieldStr.toLowerCase();
7932 value = valStr = valStr.toLowerCase();
7933 }
7934
7935 switch (operator) {
7936 case '*=':
7937 matches = fieldStr.indexOf(valStr) >= 0;
7938 break;
7939
7940 case '$=':
7941 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
7942 break;
7943
7944 case '^=':
7945 matches = fieldStr.indexOf(valStr) === 0;
7946 break;
7947
7948 case '=':
7949 matches = fieldVal === value;
7950 break;
7951
7952 case '>':
7953 isIneqCmp = true;
7954 matches = fieldVal > value;
7955 break;
7956
7957 case '>=':
7958 isIneqCmp = true;
7959 matches = fieldVal >= value;
7960 break;
7961
7962 case '<':
7963 isIneqCmp = true;
7964 matches = fieldVal < value;
7965 break;
7966
7967 case '<=':
7968 isIneqCmp = true;
7969 matches = fieldVal <= value;
7970 break;
7971
7972 default:
7973 matches = false;
7974 break;
7975 } // apply the not op, but null vals for inequalities should always stay non-matching
7976
7977
7978 if (notExpr && (fieldVal != null || !isIneqCmp)) {
7979 matches = !matches;
7980 }
7981
7982 return matches;
7983};
7984var boolCmp = function boolCmp(fieldVal, operator) {
7985 switch (operator) {
7986 case '?':
7987 return fieldVal ? true : false;
7988
7989 case '!':
7990 return fieldVal ? false : true;
7991
7992 case '^':
7993 return fieldVal === undefined;
7994 }
7995};
7996var existCmp = function existCmp(fieldVal) {
7997 return fieldVal !== undefined;
7998};
7999var data = function data(ele, field) {
8000 return ele.data(field);
8001};
8002var meta = function meta(ele, field) {
8003 return ele[field]();
8004};
8005
8006/** A lookup of `match(check, ele)` functions by `Type` int */
8007
8008var match = [];
8009/**
8010 * Returns whether the query matches for the element
8011 * @param query The `{ type, value, ... }` query object
8012 * @param ele The element to compare against
8013*/
8014
8015var matches = function matches(query, ele) {
8016 return query.checks.every(function (chk) {
8017 return match[chk.type](chk, ele);
8018 });
8019};
8020
8021match[Type.GROUP] = function (check, ele) {
8022 var group = check.value;
8023 return group === '*' || group === ele.group();
8024};
8025
8026match[Type.STATE] = function (check, ele) {
8027 var stateSelector = check.value;
8028 return stateSelectorMatches(stateSelector, ele);
8029};
8030
8031match[Type.ID] = function (check, ele) {
8032 var id = check.value;
8033 return ele.id() === id;
8034};
8035
8036match[Type.CLASS] = function (check, ele) {
8037 var cls = check.value;
8038 return ele.hasClass(cls);
8039};
8040
8041match[Type.META_COMPARE] = function (check, ele) {
8042 var field = check.field,
8043 operator = check.operator,
8044 value = check.value;
8045 return valCmp(meta(ele, field), operator, value);
8046};
8047
8048match[Type.DATA_COMPARE] = function (check, ele) {
8049 var field = check.field,
8050 operator = check.operator,
8051 value = check.value;
8052 return valCmp(data(ele, field), operator, value);
8053};
8054
8055match[Type.DATA_BOOL] = function (check, ele) {
8056 var field = check.field,
8057 operator = check.operator;
8058 return boolCmp(data(ele, field), operator);
8059};
8060
8061match[Type.DATA_EXIST] = function (check, ele) {
8062 var field = check.field,
8063 operator = check.operator;
8064 return existCmp(data(ele, field));
8065};
8066
8067match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8068 var qA = check.nodes[0];
8069 var qB = check.nodes[1];
8070 var src = ele.source();
8071 var tgt = ele.target();
8072 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
8073};
8074
8075match[Type.NODE_NEIGHBOR] = function (check, ele) {
8076 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
8077 return n.isNode() && matches(check.neighbor, n);
8078 });
8079};
8080
8081match[Type.DIRECTED_EDGE] = function (check, ele) {
8082 return matches(check.source, ele.source()) && matches(check.target, ele.target());
8083};
8084
8085match[Type.NODE_SOURCE] = function (check, ele) {
8086 return matches(check.source, ele) && ele.outgoers().some(function (n) {
8087 return n.isNode() && matches(check.target, n);
8088 });
8089};
8090
8091match[Type.NODE_TARGET] = function (check, ele) {
8092 return matches(check.target, ele) && ele.incomers().some(function (n) {
8093 return n.isNode() && matches(check.source, n);
8094 });
8095};
8096
8097match[Type.CHILD] = function (check, ele) {
8098 return matches(check.child, ele) && matches(check.parent, ele.parent());
8099};
8100
8101match[Type.PARENT] = function (check, ele) {
8102 return matches(check.parent, ele) && ele.children().some(function (c) {
8103 return matches(check.child, c);
8104 });
8105};
8106
8107match[Type.DESCENDANT] = function (check, ele) {
8108 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
8109 return matches(check.ancestor, a);
8110 });
8111};
8112
8113match[Type.ANCESTOR] = function (check, ele) {
8114 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
8115 return matches(check.descendant, d);
8116 });
8117};
8118
8119match[Type.COMPOUND_SPLIT] = function (check, ele) {
8120 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
8121};
8122
8123match[Type.TRUE] = function () {
8124 return true;
8125};
8126
8127match[Type.COLLECTION] = function (check, ele) {
8128 var collection = check.value;
8129 return collection.has(ele);
8130};
8131
8132match[Type.FILTER] = function (check, ele) {
8133 var filter = check.value;
8134 return filter(ele);
8135};
8136
8137var filter = function filter(collection) {
8138 var self = this; // for 1 id #foo queries, just get the element
8139
8140 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8141 return collection.getElementById(self[0].checks[0].value).collection();
8142 }
8143
8144 var selectorFunction = function selectorFunction(element) {
8145 for (var j = 0; j < self.length; j++) {
8146 var query = self[j];
8147
8148 if (matches(query, element)) {
8149 return true;
8150 }
8151 }
8152
8153 return false;
8154 };
8155
8156 if (self.text() == null) {
8157 selectorFunction = function selectorFunction() {
8158 return true;
8159 };
8160 }
8161
8162 return collection.filter(selectorFunction);
8163}; // filter
8164// does selector match a single element?
8165
8166
8167var matches$1 = function matches$1(ele) {
8168 var self = this;
8169
8170 for (var j = 0; j < self.length; j++) {
8171 var query = self[j];
8172
8173 if (matches(query, ele)) {
8174 return true;
8175 }
8176 }
8177
8178 return false;
8179}; // matches
8180
8181
8182var matching = {
8183 matches: matches$1,
8184 filter: filter
8185};
8186
8187var Selector = function Selector(selector) {
8188 this.inputText = selector;
8189 this.currentSubject = null;
8190 this.compoundCount = 0;
8191 this.edgeCount = 0;
8192 this.length = 0;
8193
8194 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8195 this.addQuery({
8196 checks: [{
8197 type: Type.COLLECTION,
8198 value: selector.collection()
8199 }]
8200 });
8201 } else if (fn(selector)) {
8202 this.addQuery({
8203 checks: [{
8204 type: Type.FILTER,
8205 value: selector
8206 }]
8207 });
8208 } else if (string(selector)) {
8209 if (!this.parse(selector)) {
8210 this.invalid = true;
8211 }
8212 } else {
8213 error('A selector must be created from a string; found ');
8214 }
8215};
8216
8217var selfn = Selector.prototype;
8218[parse$1, matching].forEach(function (p) {
8219 return extend(selfn, p);
8220});
8221
8222selfn.text = function () {
8223 return this.inputText;
8224};
8225
8226selfn.size = function () {
8227 return this.length;
8228};
8229
8230selfn.eq = function (i) {
8231 return this[i];
8232};
8233
8234selfn.sameText = function (otherSel) {
8235 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8236};
8237
8238selfn.addQuery = function (q) {
8239 this[this.length++] = q;
8240};
8241
8242selfn.selector = selfn.toString;
8243
8244var elesfn$f = {
8245 allAre: function allAre(selector) {
8246 var selObj = new Selector(selector);
8247 return this.every(function (ele) {
8248 return selObj.matches(ele);
8249 });
8250 },
8251 is: function is(selector) {
8252 var selObj = new Selector(selector);
8253 return this.some(function (ele) {
8254 return selObj.matches(ele);
8255 });
8256 },
8257 some: function some(fn, thisArg) {
8258 for (var i = 0; i < this.length; i++) {
8259 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8260
8261 if (ret) {
8262 return true;
8263 }
8264 }
8265
8266 return false;
8267 },
8268 every: function every(fn, thisArg) {
8269 for (var i = 0; i < this.length; i++) {
8270 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8271
8272 if (!ret) {
8273 return false;
8274 }
8275 }
8276
8277 return true;
8278 },
8279 same: function same(collection) {
8280 // cheap collection ref check
8281 if (this === collection) {
8282 return true;
8283 }
8284
8285 collection = this.cy().collection(collection);
8286 var thisLength = this.length;
8287 var collectionLength = collection.length; // cheap length check
8288
8289 if (thisLength !== collectionLength) {
8290 return false;
8291 } // cheap element ref check
8292
8293
8294 if (thisLength === 1) {
8295 return this[0] === collection[0];
8296 }
8297
8298 return this.every(function (ele) {
8299 return collection.hasElementWithId(ele.id());
8300 });
8301 },
8302 anySame: function anySame(collection) {
8303 collection = this.cy().collection(collection);
8304 return this.some(function (ele) {
8305 return collection.hasElementWithId(ele.id());
8306 });
8307 },
8308 allAreNeighbors: function allAreNeighbors(collection) {
8309 collection = this.cy().collection(collection);
8310 var nhood = this.neighborhood();
8311 return collection.every(function (ele) {
8312 return nhood.hasElementWithId(ele.id());
8313 });
8314 },
8315 contains: function contains(collection) {
8316 collection = this.cy().collection(collection);
8317 var self = this;
8318 return collection.every(function (ele) {
8319 return self.hasElementWithId(ele.id());
8320 });
8321 }
8322};
8323elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
8324elesfn$f.has = elesfn$f.contains;
8325elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
8326
8327var cache = function cache(fn, name) {
8328 return function traversalCache(arg1, arg2, arg3, arg4) {
8329 var selectorOrEles = arg1;
8330 var eles = this;
8331 var key;
8332
8333 if (selectorOrEles == null) {
8334 key = '';
8335 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
8336 key = selectorOrEles.id();
8337 }
8338
8339 if (eles.length === 1 && key) {
8340 var _p = eles[0]._private;
8341 var tch = _p.traversalCache = _p.traversalCache || {};
8342 var ch = tch[name] = tch[name] || [];
8343 var hash = hashString(key);
8344 var cacheHit = ch[hash];
8345
8346 if (cacheHit) {
8347 return cacheHit;
8348 } else {
8349 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
8350 }
8351 } else {
8352 return fn.call(eles, arg1, arg2, arg3, arg4);
8353 }
8354 };
8355};
8356
8357var elesfn$g = {
8358 parent: function parent(selector) {
8359 var parents = []; // optimisation for single ele call
8360
8361 if (this.length === 1) {
8362 var parent = this[0]._private.parent;
8363
8364 if (parent) {
8365 return parent;
8366 }
8367 }
8368
8369 for (var i = 0; i < this.length; i++) {
8370 var ele = this[i];
8371 var _parent = ele._private.parent;
8372
8373 if (_parent) {
8374 parents.push(_parent);
8375 }
8376 }
8377
8378 return this.spawn(parents, {
8379 unique: true
8380 }).filter(selector);
8381 },
8382 parents: function parents(selector) {
8383 var parents = [];
8384 var eles = this.parent();
8385
8386 while (eles.nonempty()) {
8387 for (var i = 0; i < eles.length; i++) {
8388 var ele = eles[i];
8389 parents.push(ele);
8390 }
8391
8392 eles = eles.parent();
8393 }
8394
8395 return this.spawn(parents, {
8396 unique: true
8397 }).filter(selector);
8398 },
8399 commonAncestors: function commonAncestors(selector) {
8400 var ancestors;
8401
8402 for (var i = 0; i < this.length; i++) {
8403 var ele = this[i];
8404 var parents = ele.parents();
8405 ancestors = ancestors || parents;
8406 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
8407 }
8408
8409 return ancestors.filter(selector);
8410 },
8411 orphans: function orphans(selector) {
8412 return this.stdFilter(function (ele) {
8413 return ele.isOrphan();
8414 }).filter(selector);
8415 },
8416 nonorphans: function nonorphans(selector) {
8417 return this.stdFilter(function (ele) {
8418 return ele.isChild();
8419 }).filter(selector);
8420 },
8421 children: cache(function (selector) {
8422 var children = [];
8423
8424 for (var i = 0; i < this.length; i++) {
8425 var ele = this[i];
8426 var eleChildren = ele._private.children;
8427
8428 for (var j = 0; j < eleChildren.length; j++) {
8429 children.push(eleChildren[j]);
8430 }
8431 }
8432
8433 return this.spawn(children, {
8434 unique: true
8435 }).filter(selector);
8436 }, 'children'),
8437 siblings: function siblings(selector) {
8438 return this.parent().children().not(this).filter(selector);
8439 },
8440 isParent: function isParent() {
8441 var ele = this[0];
8442
8443 if (ele) {
8444 return ele.isNode() && ele._private.children.length !== 0;
8445 }
8446 },
8447 isChildless: function isChildless() {
8448 var ele = this[0];
8449
8450 if (ele) {
8451 return ele.isNode() && ele._private.children.length === 0;
8452 }
8453 },
8454 isChild: function isChild() {
8455 var ele = this[0];
8456
8457 if (ele) {
8458 return ele.isNode() && ele._private.parent != null;
8459 }
8460 },
8461 isOrphan: function isOrphan() {
8462 var ele = this[0];
8463
8464 if (ele) {
8465 return ele.isNode() && ele._private.parent == null;
8466 }
8467 },
8468 descendants: function descendants(selector) {
8469 var elements = [];
8470
8471 function add(eles) {
8472 for (var i = 0; i < eles.length; i++) {
8473 var ele = eles[i];
8474 elements.push(ele);
8475
8476 if (ele.children().nonempty()) {
8477 add(ele.children());
8478 }
8479 }
8480 }
8481
8482 add(this.children());
8483 return this.spawn(elements, {
8484 unique: true
8485 }).filter(selector);
8486 }
8487};
8488
8489function forEachCompound(eles, fn, includeSelf, recursiveStep) {
8490 var q = [];
8491 var did = new Set$1();
8492 var cy = eles.cy();
8493 var hasCompounds = cy.hasCompoundNodes();
8494
8495 for (var i = 0; i < eles.length; i++) {
8496 var ele = eles[i];
8497
8498 if (includeSelf) {
8499 q.push(ele);
8500 } else if (hasCompounds) {
8501 recursiveStep(q, did, ele);
8502 }
8503 }
8504
8505 while (q.length > 0) {
8506 var _ele = q.shift();
8507
8508 fn(_ele);
8509 did.add(_ele.id());
8510
8511 if (hasCompounds) {
8512 recursiveStep(q, did, _ele);
8513 }
8514 }
8515
8516 return eles;
8517}
8518
8519function addChildren(q, did, ele) {
8520 if (ele.isParent()) {
8521 var children = ele._private.children;
8522
8523 for (var i = 0; i < children.length; i++) {
8524 var child = children[i];
8525
8526 if (!did.has(child.id())) {
8527 q.push(child);
8528 }
8529 }
8530 }
8531} // very efficient version of eles.add( eles.descendants() ).forEach()
8532// for internal use
8533
8534
8535elesfn$g.forEachDown = function (fn) {
8536 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8537 return forEachCompound(this, fn, includeSelf, addChildren);
8538};
8539
8540function addParent(q, did, ele) {
8541 if (ele.isChild()) {
8542 var parent = ele._private.parent;
8543
8544 if (!did.has(parent.id())) {
8545 q.push(parent);
8546 }
8547 }
8548}
8549
8550elesfn$g.forEachUp = function (fn) {
8551 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8552 return forEachCompound(this, fn, includeSelf, addParent);
8553};
8554
8555function addParentAndChildren(q, did, ele) {
8556 addParent(q, did, ele);
8557 addChildren(q, did, ele);
8558}
8559
8560elesfn$g.forEachUpAndDown = function (fn) {
8561 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8562 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
8563}; // aliases
8564
8565
8566elesfn$g.ancestors = elesfn$g.parents;
8567
8568var fn$1, elesfn$h;
8569fn$1 = elesfn$h = {
8570 data: define$3.data({
8571 field: 'data',
8572 bindingEvent: 'data',
8573 allowBinding: true,
8574 allowSetting: true,
8575 settingEvent: 'data',
8576 settingTriggersEvent: true,
8577 triggerFnName: 'trigger',
8578 allowGetting: true,
8579 immutableKeys: {
8580 'id': true,
8581 'source': true,
8582 'target': true,
8583 'parent': true
8584 },
8585 updateStyle: true
8586 }),
8587 removeData: define$3.removeData({
8588 field: 'data',
8589 event: 'data',
8590 triggerFnName: 'trigger',
8591 triggerEvent: true,
8592 immutableKeys: {
8593 'id': true,
8594 'source': true,
8595 'target': true,
8596 'parent': true
8597 },
8598 updateStyle: true
8599 }),
8600 scratch: define$3.data({
8601 field: 'scratch',
8602 bindingEvent: 'scratch',
8603 allowBinding: true,
8604 allowSetting: true,
8605 settingEvent: 'scratch',
8606 settingTriggersEvent: true,
8607 triggerFnName: 'trigger',
8608 allowGetting: true,
8609 updateStyle: true
8610 }),
8611 removeScratch: define$3.removeData({
8612 field: 'scratch',
8613 event: 'scratch',
8614 triggerFnName: 'trigger',
8615 triggerEvent: true,
8616 updateStyle: true
8617 }),
8618 rscratch: define$3.data({
8619 field: 'rscratch',
8620 allowBinding: false,
8621 allowSetting: true,
8622 settingTriggersEvent: false,
8623 allowGetting: true
8624 }),
8625 removeRscratch: define$3.removeData({
8626 field: 'rscratch',
8627 triggerEvent: false
8628 }),
8629 id: function id() {
8630 var ele = this[0];
8631
8632 if (ele) {
8633 return ele._private.data.id;
8634 }
8635 }
8636}; // aliases
8637
8638fn$1.attr = fn$1.data;
8639fn$1.removeAttr = fn$1.removeData;
8640var data$1 = elesfn$h;
8641
8642var elesfn$i = {};
8643
8644function defineDegreeFunction(callback) {
8645 return function (includeLoops) {
8646 var self = this;
8647
8648 if (includeLoops === undefined) {
8649 includeLoops = true;
8650 }
8651
8652 if (self.length === 0) {
8653 return;
8654 }
8655
8656 if (self.isNode() && !self.removed()) {
8657 var degree = 0;
8658 var node = self[0];
8659 var connectedEdges = node._private.edges;
8660
8661 for (var i = 0; i < connectedEdges.length; i++) {
8662 var edge = connectedEdges[i];
8663
8664 if (!includeLoops && edge.isLoop()) {
8665 continue;
8666 }
8667
8668 degree += callback(node, edge);
8669 }
8670
8671 return degree;
8672 } else {
8673 return;
8674 }
8675 };
8676}
8677
8678extend(elesfn$i, {
8679 degree: defineDegreeFunction(function (node, edge) {
8680 if (edge.source().same(edge.target())) {
8681 return 2;
8682 } else {
8683 return 1;
8684 }
8685 }),
8686 indegree: defineDegreeFunction(function (node, edge) {
8687 if (edge.target().same(node)) {
8688 return 1;
8689 } else {
8690 return 0;
8691 }
8692 }),
8693 outdegree: defineDegreeFunction(function (node, edge) {
8694 if (edge.source().same(node)) {
8695 return 1;
8696 } else {
8697 return 0;
8698 }
8699 })
8700});
8701
8702function defineDegreeBoundsFunction(degreeFn, callback) {
8703 return function (includeLoops) {
8704 var ret;
8705 var nodes = this.nodes();
8706
8707 for (var i = 0; i < nodes.length; i++) {
8708 var ele = nodes[i];
8709 var degree = ele[degreeFn](includeLoops);
8710
8711 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
8712 ret = degree;
8713 }
8714 }
8715
8716 return ret;
8717 };
8718}
8719
8720extend(elesfn$i, {
8721 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
8722 return degree < min;
8723 }),
8724 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
8725 return degree > max;
8726 }),
8727 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
8728 return degree < min;
8729 }),
8730 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
8731 return degree > max;
8732 }),
8733 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
8734 return degree < min;
8735 }),
8736 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
8737 return degree > max;
8738 })
8739});
8740extend(elesfn$i, {
8741 totalDegree: function totalDegree(includeLoops) {
8742 var total = 0;
8743 var nodes = this.nodes();
8744
8745 for (var i = 0; i < nodes.length; i++) {
8746 total += nodes[i].degree(includeLoops);
8747 }
8748
8749 return total;
8750 }
8751});
8752
8753var fn$2, elesfn$j;
8754
8755var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
8756 for (var i = 0; i < eles.length; i++) {
8757 var ele = eles[i];
8758
8759 if (!ele.locked()) {
8760 var oldPos = ele._private.position;
8761 var delta = {
8762 x: newPos.x != null ? newPos.x - oldPos.x : 0,
8763 y: newPos.y != null ? newPos.y - oldPos.y : 0
8764 };
8765
8766 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
8767 ele.children().shift(delta, silent);
8768 }
8769
8770 ele.shiftCachedBoundingBox(delta);
8771 }
8772 }
8773};
8774
8775var positionDef = {
8776 field: 'position',
8777 bindingEvent: 'position',
8778 allowBinding: true,
8779 allowSetting: true,
8780 settingEvent: 'position',
8781 settingTriggersEvent: true,
8782 triggerFnName: 'emitAndNotify',
8783 allowGetting: true,
8784 validKeys: ['x', 'y'],
8785 beforeGet: function beforeGet(ele) {
8786 ele.updateCompoundBounds();
8787 },
8788 beforeSet: function beforeSet(eles, newPos) {
8789 beforePositionSet(eles, newPos, false);
8790 },
8791 onSet: function onSet(eles) {
8792 eles.dirtyCompoundBoundsCache();
8793 },
8794 canSet: function canSet(ele) {
8795 return !ele.locked();
8796 }
8797};
8798fn$2 = elesfn$j = {
8799 position: define$3.data(positionDef),
8800 // position but no notification to renderer
8801 silentPosition: define$3.data(extend({}, positionDef, {
8802 allowBinding: false,
8803 allowSetting: true,
8804 settingTriggersEvent: false,
8805 allowGetting: false,
8806 beforeSet: function beforeSet(eles, newPos) {
8807 beforePositionSet(eles, newPos, true);
8808 }
8809 })),
8810 positions: function positions(pos, silent) {
8811 if (plainObject(pos)) {
8812 if (silent) {
8813 this.silentPosition(pos);
8814 } else {
8815 this.position(pos);
8816 }
8817 } else if (fn(pos)) {
8818 var _fn = pos;
8819 var cy = this.cy();
8820 cy.startBatch();
8821
8822 for (var i = 0; i < this.length; i++) {
8823 var ele = this[i];
8824
8825 var _pos = void 0;
8826
8827 if (_pos = _fn(ele, i)) {
8828 if (silent) {
8829 ele.silentPosition(_pos);
8830 } else {
8831 ele.position(_pos);
8832 }
8833 }
8834 }
8835
8836 cy.endBatch();
8837 }
8838
8839 return this; // chaining
8840 },
8841 silentPositions: function silentPositions(pos) {
8842 return this.positions(pos, true);
8843 },
8844 shift: function shift(dim, val, silent) {
8845 var delta;
8846
8847 if (plainObject(dim)) {
8848 delta = {
8849 x: number(dim.x) ? dim.x : 0,
8850 y: number(dim.y) ? dim.y : 0
8851 };
8852 silent = val;
8853 } else if (string(dim) && number(val)) {
8854 delta = {
8855 x: 0,
8856 y: 0
8857 };
8858 delta[dim] = val;
8859 }
8860
8861 if (delta != null) {
8862 var cy = this.cy();
8863 cy.startBatch();
8864
8865 for (var i = 0; i < this.length; i++) {
8866 var ele = this[i];
8867 var pos = ele.position();
8868 var newPos = {
8869 x: pos.x + delta.x,
8870 y: pos.y + delta.y
8871 };
8872
8873 if (silent) {
8874 ele.silentPosition(newPos);
8875 } else {
8876 ele.position(newPos);
8877 }
8878 }
8879
8880 cy.endBatch();
8881 }
8882
8883 return this;
8884 },
8885 silentShift: function silentShift(dim, val) {
8886 if (plainObject(dim)) {
8887 this.shift(dim, true);
8888 } else if (string(dim) && number(val)) {
8889 this.shift(dim, val, true);
8890 }
8891
8892 return this;
8893 },
8894 // get/set the rendered (i.e. on screen) positon of the element
8895 renderedPosition: function renderedPosition(dim, val) {
8896 var ele = this[0];
8897 var cy = this.cy();
8898 var zoom = cy.zoom();
8899 var pan = cy.pan();
8900 var rpos = plainObject(dim) ? dim : undefined;
8901 var setting = rpos !== undefined || val !== undefined && string(dim);
8902
8903 if (ele && ele.isNode()) {
8904 // must have an element and must be a node to return position
8905 if (setting) {
8906 for (var i = 0; i < this.length; i++) {
8907 var _ele = this[i];
8908
8909 if (val !== undefined) {
8910 // set one dimension
8911 _ele.position(dim, (val - pan[dim]) / zoom);
8912 } else if (rpos !== undefined) {
8913 // set whole position
8914 _ele.position(renderedToModelPosition(rpos, zoom, pan));
8915 }
8916 }
8917 } else {
8918 // getting
8919 var pos = ele.position();
8920 rpos = modelToRenderedPosition(pos, zoom, pan);
8921
8922 if (dim === undefined) {
8923 // then return the whole rendered position
8924 return rpos;
8925 } else {
8926 // then return the specified dimension
8927 return rpos[dim];
8928 }
8929 }
8930 } else if (!setting) {
8931 return undefined; // for empty collection case
8932 }
8933
8934 return this; // chaining
8935 },
8936 // get/set the position relative to the parent
8937 relativePosition: function relativePosition(dim, val) {
8938 var ele = this[0];
8939 var cy = this.cy();
8940 var ppos = plainObject(dim) ? dim : undefined;
8941 var setting = ppos !== undefined || val !== undefined && string(dim);
8942 var hasCompoundNodes = cy.hasCompoundNodes();
8943
8944 if (ele && ele.isNode()) {
8945 // must have an element and must be a node to return position
8946 if (setting) {
8947 for (var i = 0; i < this.length; i++) {
8948 var _ele2 = this[i];
8949 var parent = hasCompoundNodes ? _ele2.parent() : null;
8950 var hasParent = parent && parent.length > 0;
8951 var relativeToParent = hasParent;
8952
8953 if (hasParent) {
8954 parent = parent[0];
8955 }
8956
8957 var origin = relativeToParent ? parent.position() : {
8958 x: 0,
8959 y: 0
8960 };
8961
8962 if (val !== undefined) {
8963 // set one dimension
8964 _ele2.position(dim, val + origin[dim]);
8965 } else if (ppos !== undefined) {
8966 // set whole position
8967 _ele2.position({
8968 x: ppos.x + origin.x,
8969 y: ppos.y + origin.y
8970 });
8971 }
8972 }
8973 } else {
8974 // getting
8975 var pos = ele.position();
8976
8977 var _parent = hasCompoundNodes ? ele.parent() : null;
8978
8979 var _hasParent = _parent && _parent.length > 0;
8980
8981 var _relativeToParent = _hasParent;
8982
8983 if (_hasParent) {
8984 _parent = _parent[0];
8985 }
8986
8987 var _origin = _relativeToParent ? _parent.position() : {
8988 x: 0,
8989 y: 0
8990 };
8991
8992 ppos = {
8993 x: pos.x - _origin.x,
8994 y: pos.y - _origin.y
8995 };
8996
8997 if (dim === undefined) {
8998 // then return the whole rendered position
8999 return ppos;
9000 } else {
9001 // then return the specified dimension
9002 return ppos[dim];
9003 }
9004 }
9005 } else if (!setting) {
9006 return undefined; // for empty collection case
9007 }
9008
9009 return this; // chaining
9010 }
9011}; // aliases
9012
9013fn$2.modelPosition = fn$2.point = fn$2.position;
9014fn$2.modelPositions = fn$2.points = fn$2.positions;
9015fn$2.renderedPoint = fn$2.renderedPosition;
9016fn$2.relativePoint = fn$2.relativePosition;
9017var position = elesfn$j;
9018
9019var fn$3, elesfn$k;
9020fn$3 = elesfn$k = {};
9021
9022elesfn$k.renderedBoundingBox = function (options) {
9023 var bb = this.boundingBox(options);
9024 var cy = this.cy();
9025 var zoom = cy.zoom();
9026 var pan = cy.pan();
9027 var x1 = bb.x1 * zoom + pan.x;
9028 var x2 = bb.x2 * zoom + pan.x;
9029 var y1 = bb.y1 * zoom + pan.y;
9030 var y2 = bb.y2 * zoom + pan.y;
9031 return {
9032 x1: x1,
9033 x2: x2,
9034 y1: y1,
9035 y2: y2,
9036 w: x2 - x1,
9037 h: y2 - y1
9038 };
9039};
9040
9041elesfn$k.dirtyCompoundBoundsCache = function () {
9042 var cy = this.cy();
9043
9044 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9045 return this;
9046 }
9047
9048 this.forEachUp(function (ele) {
9049 if (ele.isParent()) {
9050 var _p = ele._private;
9051 _p.compoundBoundsClean = false;
9052 _p.bbCache = null;
9053 ele.emitAndNotify('bounds');
9054 }
9055 });
9056 return this;
9057};
9058
9059elesfn$k.updateCompoundBounds = function () {
9060 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9061 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9062
9063 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9064 return this;
9065 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9066
9067
9068 if (!force && cy.batching()) {
9069 return this;
9070 }
9071
9072 function update(parent) {
9073 if (!parent.isParent()) {
9074 return;
9075 }
9076
9077 var _p = parent._private;
9078 var children = parent.children();
9079 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9080 var min = {
9081 width: {
9082 val: parent.pstyle('min-width').pfValue,
9083 left: parent.pstyle('min-width-bias-left'),
9084 right: parent.pstyle('min-width-bias-right')
9085 },
9086 height: {
9087 val: parent.pstyle('min-height').pfValue,
9088 top: parent.pstyle('min-height-bias-top'),
9089 bottom: parent.pstyle('min-height-bias-bottom')
9090 }
9091 };
9092 var bb = children.boundingBox({
9093 includeLabels: includeLabels,
9094 includeOverlays: false,
9095 // updating the compound bounds happens outside of the regular
9096 // cache cycle (i.e. before fired events)
9097 useCache: false
9098 });
9099 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9100
9101 if (bb.w === 0 || bb.h === 0) {
9102 bb = {
9103 w: parent.pstyle('width').pfValue,
9104 h: parent.pstyle('height').pfValue
9105 };
9106 bb.x1 = pos.x - bb.w / 2;
9107 bb.x2 = pos.x + bb.w / 2;
9108 bb.y1 = pos.y - bb.h / 2;
9109 bb.y2 = pos.y + bb.h / 2;
9110 }
9111
9112 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9113 var biasDiff = 0;
9114 var biasComplementDiff = 0;
9115 var biasTotal = propBias + propBiasComplement;
9116
9117 if (propDiff > 0 && biasTotal > 0) {
9118 biasDiff = propBias / biasTotal * propDiff;
9119 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9120 }
9121
9122 return {
9123 biasDiff: biasDiff,
9124 biasComplementDiff: biasComplementDiff
9125 };
9126 }
9127
9128 function computePaddingValues(width, height, paddingObject, relativeTo) {
9129 // Assuming percentage is number from 0 to 1
9130 if (paddingObject.units === '%') {
9131 switch (relativeTo) {
9132 case 'width':
9133 return width > 0 ? paddingObject.pfValue * width : 0;
9134
9135 case 'height':
9136 return height > 0 ? paddingObject.pfValue * height : 0;
9137
9138 case 'average':
9139 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9140
9141 case 'min':
9142 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9143
9144 case 'max':
9145 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9146
9147 default:
9148 return 0;
9149 }
9150 } else if (paddingObject.units === 'px') {
9151 return paddingObject.pfValue;
9152 } else {
9153 return 0;
9154 }
9155 }
9156
9157 var leftVal = min.width.left.value;
9158
9159 if (min.width.left.units === 'px' && min.width.val > 0) {
9160 leftVal = leftVal * 100 / min.width.val;
9161 }
9162
9163 var rightVal = min.width.right.value;
9164
9165 if (min.width.right.units === 'px' && min.width.val > 0) {
9166 rightVal = rightVal * 100 / min.width.val;
9167 }
9168
9169 var topVal = min.height.top.value;
9170
9171 if (min.height.top.units === 'px' && min.height.val > 0) {
9172 topVal = topVal * 100 / min.height.val;
9173 }
9174
9175 var bottomVal = min.height.bottom.value;
9176
9177 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9178 bottomVal = bottomVal * 100 / min.height.val;
9179 }
9180
9181 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9182 var diffLeft = widthBiasDiffs.biasDiff;
9183 var diffRight = widthBiasDiffs.biasComplementDiff;
9184 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9185 var diffTop = heightBiasDiffs.biasDiff;
9186 var diffBottom = heightBiasDiffs.biasComplementDiff;
9187 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9188 _p.autoWidth = Math.max(bb.w, min.width.val);
9189 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9190 _p.autoHeight = Math.max(bb.h, min.height.val);
9191 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9192 }
9193
9194 for (var i = 0; i < this.length; i++) {
9195 var ele = this[i];
9196 var _p = ele._private;
9197
9198 if (!_p.compoundBoundsClean) {
9199 update(ele);
9200
9201 if (!cy.batching()) {
9202 _p.compoundBoundsClean = true;
9203 }
9204 }
9205 }
9206
9207 return this;
9208};
9209
9210var noninf = function noninf(x) {
9211 if (x === Infinity || x === -Infinity) {
9212 return 0;
9213 }
9214
9215 return x;
9216};
9217
9218var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9219 // don't update with zero area boxes
9220 if (x2 - x1 === 0 || y2 - y1 === 0) {
9221 return;
9222 } // don't update with null dim
9223
9224
9225 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9226 return;
9227 }
9228
9229 b.x1 = x1 < b.x1 ? x1 : b.x1;
9230 b.x2 = x2 > b.x2 ? x2 : b.x2;
9231 b.y1 = y1 < b.y1 ? y1 : b.y1;
9232 b.y2 = y2 > b.y2 ? y2 : b.y2;
9233 b.w = b.x2 - b.x1;
9234 b.h = b.y2 - b.y1;
9235};
9236
9237var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
9238 if (b2 == null) {
9239 return b;
9240 }
9241
9242 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
9243};
9244
9245var prefixedProperty = function prefixedProperty(obj, field, prefix) {
9246 return getPrefixedProperty(obj, field, prefix);
9247};
9248
9249var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
9250 if (ele.cy().headless()) {
9251 return;
9252 }
9253
9254 var _p = ele._private;
9255 var rstyle = _p.rstyle;
9256 var halfArW = rstyle.arrowWidth / 2;
9257 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
9258 var x;
9259 var y;
9260
9261 if (arrowType !== 'none') {
9262 if (prefix === 'source') {
9263 x = rstyle.srcX;
9264 y = rstyle.srcY;
9265 } else if (prefix === 'target') {
9266 x = rstyle.tgtX;
9267 y = rstyle.tgtY;
9268 } else {
9269 x = rstyle.midX;
9270 y = rstyle.midY;
9271 } // always store the individual arrow bounds
9272
9273
9274 var bbs = _p.arrowBounds = _p.arrowBounds || {};
9275 var bb = bbs[prefix] = bbs[prefix] || {};
9276 bb.x1 = x - halfArW;
9277 bb.y1 = y - halfArW;
9278 bb.x2 = x + halfArW;
9279 bb.y2 = y + halfArW;
9280 bb.w = bb.x2 - bb.x1;
9281 bb.h = bb.y2 - bb.y1;
9282 expandBoundingBox(bb, 1);
9283 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
9284 }
9285};
9286
9287var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
9288 if (ele.cy().headless()) {
9289 return;
9290 }
9291
9292 var prefixDash;
9293
9294 if (prefix) {
9295 prefixDash = prefix + '-';
9296 } else {
9297 prefixDash = '';
9298 }
9299
9300 var _p = ele._private;
9301 var rstyle = _p.rstyle;
9302 var label = ele.pstyle(prefixDash + 'label').strValue;
9303
9304 if (label) {
9305 var halign = ele.pstyle('text-halign');
9306 var valign = ele.pstyle('text-valign');
9307 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
9308 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
9309 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
9310 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
9311 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
9312 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
9313 var isEdge = ele.isEdge();
9314 var rotation = ele.pstyle(prefixDash + 'text-rotation');
9315 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
9316 var borderWidth = ele.pstyle('text-border-width').pfValue;
9317 var halfBorderWidth = borderWidth / 2;
9318 var padding = ele.pstyle('text-background-padding').pfValue;
9319 var lh = labelHeight;
9320 var lw = labelWidth;
9321 var lw_2 = lw / 2;
9322 var lh_2 = lh / 2;
9323 var lx1, lx2, ly1, ly2;
9324
9325 if (isEdge) {
9326 lx1 = labelX - lw_2;
9327 lx2 = labelX + lw_2;
9328 ly1 = labelY - lh_2;
9329 ly2 = labelY + lh_2;
9330 } else {
9331 switch (halign.value) {
9332 case 'left':
9333 lx1 = labelX - lw;
9334 lx2 = labelX;
9335 break;
9336
9337 case 'center':
9338 lx1 = labelX - lw_2;
9339 lx2 = labelX + lw_2;
9340 break;
9341
9342 case 'right':
9343 lx1 = labelX;
9344 lx2 = labelX + lw;
9345 break;
9346 }
9347
9348 switch (valign.value) {
9349 case 'top':
9350 ly1 = labelY - lh;
9351 ly2 = labelY;
9352 break;
9353
9354 case 'center':
9355 ly1 = labelY - lh_2;
9356 ly2 = labelY + lh_2;
9357 break;
9358
9359 case 'bottom':
9360 ly1 = labelY;
9361 ly2 = labelY + lh;
9362 break;
9363 }
9364 } // shift by margin and expand by outline and border
9365
9366
9367 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding;
9368 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding;
9369 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding;
9370 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding; // always store the unrotated label bounds separately
9371
9372 var bbPrefix = prefix || 'main';
9373 var bbs = _p.labelBounds;
9374 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
9375 bb.x1 = lx1;
9376 bb.y1 = ly1;
9377 bb.x2 = lx2;
9378 bb.y2 = ly2;
9379 bb.w = lx2 - lx1;
9380 bb.h = ly2 - ly1;
9381 expandBoundingBox(bb, 1); // expand to work around browser dimension inaccuracies
9382
9383 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
9384 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
9385
9386 if (isAutorotate || isPfValue) {
9387 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
9388 var cos = Math.cos(theta);
9389 var sin = Math.sin(theta); // rotation point (default value for center-center)
9390
9391 var xo = (lx1 + lx2) / 2;
9392 var yo = (ly1 + ly2) / 2;
9393
9394 if (!isEdge) {
9395 switch (halign.value) {
9396 case 'left':
9397 xo = lx2;
9398 break;
9399
9400 case 'right':
9401 xo = lx1;
9402 break;
9403 }
9404
9405 switch (valign.value) {
9406 case 'top':
9407 yo = ly2;
9408 break;
9409
9410 case 'bottom':
9411 yo = ly1;
9412 break;
9413 }
9414 }
9415
9416 var rotate = function rotate(x, y) {
9417 x = x - xo;
9418 y = y - yo;
9419 return {
9420 x: x * cos - y * sin + xo,
9421 y: x * sin + y * cos + yo
9422 };
9423 };
9424
9425 var px1y1 = rotate(lx1, ly1);
9426 var px1y2 = rotate(lx1, ly2);
9427 var px2y1 = rotate(lx2, ly1);
9428 var px2y2 = rotate(lx2, ly2);
9429 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9430 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9431 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9432 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9433 }
9434
9435 var bbPrefixRot = bbPrefix + 'Rot';
9436 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
9437 bbRot.x1 = lx1;
9438 bbRot.y1 = ly1;
9439 bbRot.x2 = lx2;
9440 bbRot.y2 = ly2;
9441 bbRot.w = lx2 - lx1;
9442 bbRot.h = ly2 - ly1;
9443 updateBounds(bounds, lx1, ly1, lx2, ly2);
9444 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
9445 }
9446
9447 return bounds;
9448}; // get the bounding box of the elements (in raw model position)
9449
9450
9451var boundingBoxImpl = function boundingBoxImpl(ele, options) {
9452 var cy = ele._private.cy;
9453 var styleEnabled = cy.styleEnabled();
9454 var headless = cy.headless();
9455 var bounds = makeBoundingBox();
9456 var _p = ele._private;
9457 var isNode = ele.isNode();
9458 var isEdge = ele.isEdge();
9459 var ex1, ex2, ey1, ey2; // extrema of body / lines
9460
9461 var x, y; // node pos
9462
9463 var rstyle = _p.rstyle;
9464 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
9465 // (other factors like width values will be considered later in this function anyway)
9466
9467 var isDisplayed = function isDisplayed(ele) {
9468 return ele.pstyle('display').value !== 'none';
9469 };
9470
9471 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
9472 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
9473
9474 if (displayed) {
9475 // displayed suffices, since we will find zero area eles anyway
9476 var overlayOpacity = 0;
9477 var overlayPadding = 0;
9478
9479 if (styleEnabled && options.includeOverlays) {
9480 overlayOpacity = ele.pstyle('overlay-opacity').value;
9481
9482 if (overlayOpacity !== 0) {
9483 overlayPadding = ele.pstyle('overlay-padding').value;
9484 }
9485 }
9486
9487 var w = 0;
9488 var wHalf = 0;
9489
9490 if (styleEnabled) {
9491 w = ele.pstyle('width').pfValue;
9492 wHalf = w / 2;
9493 }
9494
9495 if (isNode && options.includeNodes) {
9496 var pos = ele.position();
9497 x = pos.x;
9498 y = pos.y;
9499
9500 var _w = ele.outerWidth();
9501
9502 var halfW = _w / 2;
9503 var h = ele.outerHeight();
9504 var halfH = h / 2; // handle node dimensions
9505 /////////////////////////
9506
9507 ex1 = x - halfW;
9508 ex2 = x + halfW;
9509 ey1 = y - halfH;
9510 ey2 = y + halfH;
9511 updateBounds(bounds, ex1, ey1, ex2, ey2);
9512 } else if (isEdge && options.includeEdges) {
9513 if (styleEnabled && !headless) {
9514 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
9515 //////////////////////////////////////////////
9516
9517 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9518 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9519 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
9520 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
9521
9522 ex1 -= wHalf;
9523 ex2 += wHalf;
9524 ey1 -= wHalf;
9525 ey2 += wHalf;
9526 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
9527 ////////////////
9528
9529 if (curveStyle === 'haystack') {
9530 var hpts = rstyle.haystackPts;
9531
9532 if (hpts && hpts.length === 2) {
9533 ex1 = hpts[0].x;
9534 ey1 = hpts[0].y;
9535 ex2 = hpts[1].x;
9536 ey2 = hpts[1].y;
9537
9538 if (ex1 > ex2) {
9539 var temp = ex1;
9540 ex1 = ex2;
9541 ex2 = temp;
9542 }
9543
9544 if (ey1 > ey2) {
9545 var _temp = ey1;
9546 ey1 = ey2;
9547 ey2 = _temp;
9548 }
9549
9550 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
9551 }
9552 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
9553 var pts;
9554
9555 switch (curveStyle) {
9556 case 'bezier':
9557 case 'unbundled-bezier':
9558 pts = rstyle.bezierPts;
9559 break;
9560
9561 case 'segments':
9562 case 'taxi':
9563 pts = rstyle.linePts;
9564 break;
9565 }
9566
9567 if (pts != null) {
9568 for (var j = 0; j < pts.length; j++) {
9569 var pt = pts[j];
9570 ex1 = pt.x - wHalf;
9571 ex2 = pt.x + wHalf;
9572 ey1 = pt.y - wHalf;
9573 ey2 = pt.y + wHalf;
9574 updateBounds(bounds, ex1, ey1, ex2, ey2);
9575 }
9576 }
9577 } // bezier-like or segment-like edge
9578
9579 } else {
9580 // headless or style disabled
9581 // fallback on source and target positions
9582 //////////////////////////////////////////
9583 var n1 = ele.source();
9584 var n1pos = n1.position();
9585 var n2 = ele.target();
9586 var n2pos = n2.position();
9587 ex1 = n1pos.x;
9588 ex2 = n2pos.x;
9589 ey1 = n1pos.y;
9590 ey2 = n2pos.y;
9591
9592 if (ex1 > ex2) {
9593 var _temp2 = ex1;
9594 ex1 = ex2;
9595 ex2 = _temp2;
9596 }
9597
9598 if (ey1 > ey2) {
9599 var _temp3 = ey1;
9600 ey1 = ey2;
9601 ey2 = _temp3;
9602 } // take into account edge width
9603
9604
9605 ex1 -= wHalf;
9606 ex2 += wHalf;
9607 ey1 -= wHalf;
9608 ey2 += wHalf;
9609 updateBounds(bounds, ex1, ey1, ex2, ey2);
9610 } // headless or style disabled
9611
9612 } // edges
9613 // handle edge arrow size
9614 /////////////////////////
9615
9616
9617 if (styleEnabled && options.includeEdges && isEdge) {
9618 updateBoundsFromArrow(bounds, ele, 'mid-source');
9619 updateBoundsFromArrow(bounds, ele, 'mid-target');
9620 updateBoundsFromArrow(bounds, ele, 'source');
9621 updateBoundsFromArrow(bounds, ele, 'target');
9622 } // ghost
9623 ////////
9624
9625
9626 if (styleEnabled) {
9627 var ghost = ele.pstyle('ghost').value === 'yes';
9628
9629 if (ghost) {
9630 var gx = ele.pstyle('ghost-offset-x').pfValue;
9631 var gy = ele.pstyle('ghost-offset-y').pfValue;
9632 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
9633 }
9634 } // always store the body bounds separately from the labels
9635
9636
9637 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
9638 assignBoundingBox(bbBody, bounds);
9639 expandBoundingBoxSides(bbBody, manualExpansion);
9640 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
9641 // overlay
9642 //////////
9643
9644 if (styleEnabled) {
9645 ex1 = bounds.x1;
9646 ex2 = bounds.x2;
9647 ey1 = bounds.y1;
9648 ey2 = bounds.y2;
9649 updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
9650 } // always store the body bounds separately from the labels
9651
9652
9653 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
9654 assignBoundingBox(bbOverlay, bounds);
9655 expandBoundingBoxSides(bbOverlay, manualExpansion);
9656 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
9657 // handle label dimensions
9658 //////////////////////////
9659
9660 var bbLabels = _p.labelBounds = _p.labelBounds || {};
9661
9662 if (bbLabels.all != null) {
9663 clearBoundingBox(bbLabels.all);
9664 } else {
9665 bbLabels.all = makeBoundingBox();
9666 }
9667
9668 if (styleEnabled && options.includeLabels) {
9669 if (options.includeMainLabels) {
9670 updateBoundsFromLabel(bounds, ele, null);
9671 }
9672
9673 if (isEdge) {
9674 if (options.includeSourceLabels) {
9675 updateBoundsFromLabel(bounds, ele, 'source');
9676 }
9677
9678 if (options.includeTargetLabels) {
9679 updateBoundsFromLabel(bounds, ele, 'target');
9680 }
9681 }
9682 } // style enabled for labels
9683
9684 } // if displayed
9685
9686
9687 bounds.x1 = noninf(bounds.x1);
9688 bounds.y1 = noninf(bounds.y1);
9689 bounds.x2 = noninf(bounds.x2);
9690 bounds.y2 = noninf(bounds.y2);
9691 bounds.w = noninf(bounds.x2 - bounds.x1);
9692 bounds.h = noninf(bounds.y2 - bounds.y1);
9693
9694 if (bounds.w > 0 && bounds.h > 0 && displayed) {
9695 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
9696
9697 expandBoundingBox(bounds, 1);
9698 }
9699
9700 return bounds;
9701};
9702
9703var getKey = function getKey(opts) {
9704 var i = 0;
9705
9706 var tf = function tf(val) {
9707 return (val ? 1 : 0) << i++;
9708 };
9709
9710 var key = 0;
9711 key += tf(opts.incudeNodes);
9712 key += tf(opts.includeEdges);
9713 key += tf(opts.includeLabels);
9714 key += tf(opts.includeMainLabels);
9715 key += tf(opts.includeSourceLabels);
9716 key += tf(opts.includeTargetLabels);
9717 key += tf(opts.includeOverlays);
9718 return key;
9719};
9720
9721var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
9722 if (ele.isEdge()) {
9723 var p1 = ele.source().position();
9724 var p2 = ele.target().position();
9725
9726 var r = function r(x) {
9727 return Math.round(x);
9728 };
9729
9730 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
9731 } else {
9732 return 0;
9733 }
9734};
9735
9736var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
9737 var _p = ele._private;
9738 var bb;
9739 var isEdge = ele.isEdge();
9740 var key = opts == null ? defBbOptsKey : getKey(opts);
9741 var usingDefOpts = key === defBbOptsKey;
9742 var currPosKey = getBoundingBoxPosKey(ele);
9743 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9744 var useCache = opts.useCache && isPosKeySame;
9745
9746 var isDirty = function isDirty(ele) {
9747 return ele._private.bbCache == null;
9748 };
9749
9750 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
9751
9752 if (needRecalc) {
9753 if (!isPosKeySame) {
9754 ele.recalculateRenderedStyle();
9755 }
9756
9757 bb = boundingBoxImpl(ele, defBbOpts);
9758 _p.bbCache = bb;
9759 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
9760 _p.bbCachePosKey = currPosKey;
9761 } else {
9762 bb = _p.bbCache;
9763 }
9764
9765 if (!needRecalc && (_p.bbCacheShift.x !== 0 || _p.bbCacheShift.y !== 0)) {
9766 var shift = assignShiftToBoundingBox;
9767 var delta = _p.bbCacheShift;
9768
9769 var safeShift = function safeShift(bb, delta) {
9770 if (bb != null) {
9771 shift(bb, delta);
9772 }
9773 };
9774
9775 shift(bb, delta);
9776 var bodyBounds = _p.bodyBounds,
9777 overlayBounds = _p.overlayBounds,
9778 labelBounds = _p.labelBounds,
9779 arrowBounds = _p.arrowBounds;
9780 safeShift(bodyBounds, delta);
9781 safeShift(overlayBounds, delta);
9782
9783 if (arrowBounds != null) {
9784 safeShift(arrowBounds.source, delta);
9785 safeShift(arrowBounds.target, delta);
9786 safeShift(arrowBounds['mid-source'], delta);
9787 safeShift(arrowBounds['mid-target'], delta);
9788 }
9789
9790 if (labelBounds != null) {
9791 safeShift(labelBounds.main, delta);
9792 safeShift(labelBounds.all, delta);
9793 safeShift(labelBounds.source, delta);
9794 safeShift(labelBounds.target, delta);
9795 }
9796 } // always reset the shift, because we either applied the shift or cleared it by doing a fresh recalc
9797
9798
9799 _p.bbCacheShift.x = _p.bbCacheShift.y = 0; // not using def opts => need to build up bb from combination of sub bbs
9800
9801 if (!usingDefOpts) {
9802 var isNode = ele.isNode();
9803 bb = makeBoundingBox();
9804
9805 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
9806 if (opts.includeOverlays) {
9807 updateBoundsFromBox(bb, _p.overlayBounds);
9808 } else {
9809 updateBoundsFromBox(bb, _p.bodyBounds);
9810 }
9811 }
9812
9813 if (opts.includeLabels) {
9814 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
9815 updateBoundsFromBox(bb, _p.labelBounds.all);
9816 } else {
9817 if (opts.includeMainLabels) {
9818 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
9819 }
9820
9821 if (opts.includeSourceLabels) {
9822 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
9823 }
9824
9825 if (opts.includeTargetLabels) {
9826 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
9827 }
9828 }
9829 }
9830
9831 bb.w = bb.x2 - bb.x1;
9832 bb.h = bb.y2 - bb.y1;
9833 }
9834
9835 return bb;
9836};
9837
9838var defBbOpts = {
9839 includeNodes: true,
9840 includeEdges: true,
9841 includeLabels: true,
9842 includeMainLabels: true,
9843 includeSourceLabels: true,
9844 includeTargetLabels: true,
9845 includeOverlays: true,
9846 useCache: true
9847};
9848var defBbOptsKey = getKey(defBbOpts);
9849var filledBbOpts = defaults(defBbOpts);
9850
9851elesfn$k.boundingBox = function (options) {
9852 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
9853 // specified s.t. the cache is used, so check for this case to make it faster by
9854 // avoiding the overhead of the rest of the function
9855
9856 if (this.length === 1 && this[0]._private.bbCache != null && (options === undefined || options.useCache === undefined || options.useCache === true)) {
9857 if (options === undefined) {
9858 options = defBbOpts;
9859 } else {
9860 options = filledBbOpts(options);
9861 }
9862
9863 bounds = cachedBoundingBoxImpl(this[0], options);
9864 } else {
9865 bounds = makeBoundingBox();
9866 options = options || defBbOpts;
9867 var opts = filledBbOpts(options);
9868 var eles = this;
9869 var cy = eles.cy();
9870 var styleEnabled = cy.styleEnabled();
9871
9872 if (styleEnabled) {
9873 for (var i = 0; i < eles.length; i++) {
9874 var ele = eles[i];
9875 var _p = ele._private;
9876 var currPosKey = getBoundingBoxPosKey(ele);
9877 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9878 var useCache = opts.useCache && isPosKeySame;
9879 ele.recalculateRenderedStyle(useCache);
9880 }
9881 }
9882
9883 this.updateCompoundBounds();
9884
9885 for (var _i = 0; _i < eles.length; _i++) {
9886 var _ele = eles[_i];
9887 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
9888 }
9889 }
9890
9891 bounds.x1 = noninf(bounds.x1);
9892 bounds.y1 = noninf(bounds.y1);
9893 bounds.x2 = noninf(bounds.x2);
9894 bounds.y2 = noninf(bounds.y2);
9895 bounds.w = noninf(bounds.x2 - bounds.x1);
9896 bounds.h = noninf(bounds.y2 - bounds.y1);
9897 return bounds;
9898};
9899
9900elesfn$k.dirtyBoundingBoxCache = function () {
9901 for (var i = 0; i < this.length; i++) {
9902 var _p = this[i]._private;
9903 _p.bbCache = null;
9904 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
9905 _p.bbCachePosKey = null;
9906 _p.bodyBounds = null;
9907 _p.overlayBounds = null;
9908 _p.labelBounds.all = null;
9909 _p.labelBounds.source = null;
9910 _p.labelBounds.target = null;
9911 _p.labelBounds.main = null;
9912 _p.labelBounds.sourceRot = null;
9913 _p.labelBounds.targetRot = null;
9914 _p.labelBounds.mainRot = null;
9915 _p.arrowBounds.source = null;
9916 _p.arrowBounds.target = null;
9917 _p.arrowBounds['mid-source'] = null;
9918 _p.arrowBounds['mid-target'] = null;
9919 }
9920
9921 this.emitAndNotify('bounds');
9922 return this;
9923};
9924
9925elesfn$k.shiftCachedBoundingBox = function (delta) {
9926 for (var i = 0; i < this.length; i++) {
9927 var ele = this[i];
9928 var _p = ele._private;
9929 var bb = _p.bbCache;
9930
9931 if (bb != null) {
9932 _p.bbCacheShift.x += delta.x;
9933 _p.bbCacheShift.y += delta.y;
9934 }
9935 }
9936
9937 this.emitAndNotify('bounds');
9938 return this;
9939}; // private helper to get bounding box for custom node positions
9940// - good for perf in certain cases but currently requires dirtying the rendered style
9941// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
9942// - try to use for only things like discrete layouts where the node position would change anyway
9943
9944
9945elesfn$k.boundingBoxAt = function (fn) {
9946 var nodes = this.nodes();
9947 var cy = this.cy();
9948 var hasCompoundNodes = cy.hasCompoundNodes();
9949
9950 if (hasCompoundNodes) {
9951 nodes = nodes.filter(function (node) {
9952 return !node.isParent();
9953 });
9954 }
9955
9956 if (plainObject(fn)) {
9957 var obj = fn;
9958
9959 fn = function fn() {
9960 return obj;
9961 };
9962 }
9963
9964 var storeOldPos = function storeOldPos(node, i) {
9965 return node._private.bbAtOldPos = fn(node, i);
9966 };
9967
9968 var getOldPos = function getOldPos(node) {
9969 return node._private.bbAtOldPos;
9970 };
9971
9972 cy.startBatch();
9973 nodes.forEach(storeOldPos).silentPositions(fn);
9974
9975 if (hasCompoundNodes) {
9976 this.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
9977 }
9978
9979 var bb = copyBoundingBox(this.boundingBox({
9980 useCache: false
9981 }));
9982 nodes.silentPositions(getOldPos);
9983 cy.endBatch();
9984 return bb;
9985};
9986
9987fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
9988fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
9989var bounds = elesfn$k;
9990
9991var fn$4, elesfn$l;
9992fn$4 = elesfn$l = {};
9993
9994var defineDimFns = function defineDimFns(opts) {
9995 opts.uppercaseName = capitalize(opts.name);
9996 opts.autoName = 'auto' + opts.uppercaseName;
9997 opts.labelName = 'label' + opts.uppercaseName;
9998 opts.outerName = 'outer' + opts.uppercaseName;
9999 opts.uppercaseOuterName = capitalize(opts.outerName);
10000
10001 fn$4[opts.name] = function dimImpl() {
10002 var ele = this[0];
10003 var _p = ele._private;
10004 var cy = _p.cy;
10005 var styleEnabled = cy._private.styleEnabled;
10006
10007 if (ele) {
10008 if (styleEnabled) {
10009 if (ele.isParent()) {
10010 ele.updateCompoundBounds();
10011 return _p[opts.autoName] || 0;
10012 }
10013
10014 var d = ele.pstyle(opts.name);
10015
10016 switch (d.strValue) {
10017 case 'label':
10018 ele.recalculateRenderedStyle();
10019 return _p.rstyle[opts.labelName] || 0;
10020
10021 default:
10022 return d.pfValue;
10023 }
10024 } else {
10025 return 1;
10026 }
10027 }
10028 };
10029
10030 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
10031 var ele = this[0];
10032 var _p = ele._private;
10033 var cy = _p.cy;
10034 var styleEnabled = cy._private.styleEnabled;
10035
10036 if (ele) {
10037 if (styleEnabled) {
10038 var dim = ele[opts.name]();
10039 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10040
10041 var padding = 2 * ele.padding();
10042 return dim + border + padding;
10043 } else {
10044 return 1;
10045 }
10046 }
10047 };
10048
10049 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10050 var ele = this[0];
10051
10052 if (ele) {
10053 var d = ele[opts.name]();
10054 return d * this.cy().zoom();
10055 }
10056 };
10057
10058 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10059 var ele = this[0];
10060
10061 if (ele) {
10062 var od = ele[opts.outerName]();
10063 return od * this.cy().zoom();
10064 }
10065 };
10066};
10067
10068defineDimFns({
10069 name: 'width'
10070});
10071defineDimFns({
10072 name: 'height'
10073});
10074
10075elesfn$l.padding = function () {
10076 var ele = this[0];
10077 var _p = ele._private;
10078
10079 if (ele.isParent()) {
10080 ele.updateCompoundBounds();
10081
10082 if (_p.autoPadding !== undefined) {
10083 return _p.autoPadding;
10084 } else {
10085 return ele.pstyle('padding').pfValue;
10086 }
10087 } else {
10088 return ele.pstyle('padding').pfValue;
10089 }
10090};
10091
10092elesfn$l.paddedHeight = function () {
10093 var ele = this[0];
10094 return ele.height() + 2 * ele.padding();
10095};
10096
10097elesfn$l.paddedWidth = function () {
10098 var ele = this[0];
10099 return ele.width() + 2 * ele.padding();
10100};
10101
10102var widthHeight = elesfn$l;
10103
10104var ifEdge = function ifEdge(ele, getValue) {
10105 if (ele.isEdge()) {
10106 return getValue(ele);
10107 }
10108};
10109
10110var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10111 if (ele.isEdge()) {
10112 var cy = ele.cy();
10113 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10114 }
10115};
10116
10117var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10118 if (ele.isEdge()) {
10119 var cy = ele.cy();
10120 var pan = cy.pan();
10121 var zoom = cy.zoom();
10122 return getPoints(ele).map(function (p) {
10123 return modelToRenderedPosition(p, zoom, pan);
10124 });
10125 }
10126};
10127
10128var controlPoints = function controlPoints(ele) {
10129 return ele.renderer().getControlPoints(ele);
10130};
10131
10132var segmentPoints = function segmentPoints(ele) {
10133 return ele.renderer().getSegmentPoints(ele);
10134};
10135
10136var sourceEndpoint = function sourceEndpoint(ele) {
10137 return ele.renderer().getSourceEndpoint(ele);
10138};
10139
10140var targetEndpoint = function targetEndpoint(ele) {
10141 return ele.renderer().getTargetEndpoint(ele);
10142};
10143
10144var midpoint = function midpoint(ele) {
10145 return ele.renderer().getEdgeMidpoint(ele);
10146};
10147
10148var pts = {
10149 controlPoints: {
10150 get: controlPoints,
10151 mult: true
10152 },
10153 segmentPoints: {
10154 get: segmentPoints,
10155 mult: true
10156 },
10157 sourceEndpoint: {
10158 get: sourceEndpoint
10159 },
10160 targetEndpoint: {
10161 get: targetEndpoint
10162 },
10163 midpoint: {
10164 get: midpoint
10165 }
10166};
10167
10168var renderedName = function renderedName(name) {
10169 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10170};
10171
10172var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10173 var spec = pts[name];
10174 var rName = renderedName(name);
10175
10176 obj[name] = function () {
10177 return ifEdge(this, spec.get);
10178 };
10179
10180 if (spec.mult) {
10181 obj[rName] = function () {
10182 return ifEdgeRenderedPositions(this, spec.get);
10183 };
10184 } else {
10185 obj[rName] = function () {
10186 return ifEdgeRenderedPosition(this, spec.get);
10187 };
10188 }
10189
10190 return obj;
10191}, {});
10192
10193var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10194
10195/*!
10196Event object based on jQuery events, MIT license
10197
10198https://jquery.org/license/
10199https://tldrlegal.com/license/mit-license
10200https://github.com/jquery/jquery/blob/master/src/event.js
10201*/
10202var Event = function Event(src, props) {
10203 this.recycle(src, props);
10204};
10205
10206function returnFalse() {
10207 return false;
10208}
10209
10210function returnTrue() {
10211 return true;
10212} // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10213
10214
10215Event.prototype = {
10216 instanceString: function instanceString() {
10217 return 'event';
10218 },
10219 recycle: function recycle(src, props) {
10220 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10221
10222 if (src != null && src.preventDefault) {
10223 // Browser Event object
10224 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10225 // by a handler lower down the tree; reflect the correct value.
10226
10227 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10228 } else if (src != null && src.type) {
10229 // Plain object containing all event details
10230 props = src;
10231 } else {
10232 // Event string
10233 this.type = src;
10234 } // Put explicitly provided properties onto the event object
10235
10236
10237 if (props != null) {
10238 // more efficient to manually copy fields we use
10239 this.originalEvent = props.originalEvent;
10240 this.type = props.type != null ? props.type : this.type;
10241 this.cy = props.cy;
10242 this.target = props.target;
10243 this.position = props.position;
10244 this.renderedPosition = props.renderedPosition;
10245 this.namespace = props.namespace;
10246 this.layout = props.layout;
10247 }
10248
10249 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10250 // create a rendered position based on the passed position
10251 var pos = this.position;
10252 var zoom = this.cy.zoom();
10253 var pan = this.cy.pan();
10254 this.renderedPosition = {
10255 x: pos.x * zoom + pan.x,
10256 y: pos.y * zoom + pan.y
10257 };
10258 } // Create a timestamp if incoming event doesn't have one
10259
10260
10261 this.timeStamp = src && src.timeStamp || Date.now();
10262 },
10263 preventDefault: function preventDefault() {
10264 this.isDefaultPrevented = returnTrue;
10265 var e = this.originalEvent;
10266
10267 if (!e) {
10268 return;
10269 } // if preventDefault exists run it on the original event
10270
10271
10272 if (e.preventDefault) {
10273 e.preventDefault();
10274 }
10275 },
10276 stopPropagation: function stopPropagation() {
10277 this.isPropagationStopped = returnTrue;
10278 var e = this.originalEvent;
10279
10280 if (!e) {
10281 return;
10282 } // if stopPropagation exists run it on the original event
10283
10284
10285 if (e.stopPropagation) {
10286 e.stopPropagation();
10287 }
10288 },
10289 stopImmediatePropagation: function stopImmediatePropagation() {
10290 this.isImmediatePropagationStopped = returnTrue;
10291 this.stopPropagation();
10292 },
10293 isDefaultPrevented: returnFalse,
10294 isPropagationStopped: returnFalse,
10295 isImmediatePropagationStopped: returnFalse
10296};
10297
10298var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
10299
10300var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
10301
10302var defaults$8 = {
10303 qualifierCompare: function qualifierCompare(q1, q2) {
10304 return q1 === q2;
10305 },
10306 eventMatches: function eventMatches()
10307 /*context, listener, eventObj*/
10308 {
10309 return true;
10310 },
10311 addEventFields: function addEventFields()
10312 /*context, evt*/
10313 {},
10314 callbackContext: function callbackContext(context
10315 /*, listener, eventObj*/
10316 ) {
10317 return context;
10318 },
10319 beforeEmit: function beforeEmit()
10320 /* context, listener, eventObj */
10321 {},
10322 afterEmit: function afterEmit()
10323 /* context, listener, eventObj */
10324 {},
10325 bubble: function bubble()
10326 /*context*/
10327 {
10328 return false;
10329 },
10330 parent: function parent()
10331 /*context*/
10332 {
10333 return null;
10334 },
10335 context: null
10336};
10337var defaultsKeys = Object.keys(defaults$8);
10338var emptyOpts = {};
10339
10340function Emitter() {
10341 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
10342 var context = arguments.length > 1 ? arguments[1] : undefined;
10343
10344 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
10345 for (var i = 0; i < defaultsKeys.length; i++) {
10346 var key = defaultsKeys[i];
10347 this[key] = opts[key] || defaults$8[key];
10348 }
10349
10350 this.context = context || this.context;
10351 this.listeners = [];
10352 this.emitting = 0;
10353}
10354
10355var p = Emitter.prototype;
10356
10357var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
10358 if (fn(qualifier)) {
10359 callback = qualifier;
10360 qualifier = null;
10361 }
10362
10363 if (confOverrides) {
10364 if (conf == null) {
10365 conf = confOverrides;
10366 } else {
10367 conf = extend({}, conf, confOverrides);
10368 }
10369 }
10370
10371 var eventList = array(events) ? events : events.split(/\s+/);
10372
10373 for (var i = 0; i < eventList.length; i++) {
10374 var evt = eventList[i];
10375
10376 if (emptyString(evt)) {
10377 continue;
10378 }
10379
10380 var match = evt.match(eventRegex); // type[.namespace]
10381
10382 if (match) {
10383 var type = match[1];
10384 var namespace = match[2] ? match[2] : null;
10385 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
10386
10387 if (ret === false) {
10388 break;
10389 } // allow exiting early
10390
10391 }
10392 }
10393};
10394
10395var makeEventObj = function makeEventObj(self, obj) {
10396 self.addEventFields(self.context, obj);
10397 return new Event(obj.type, obj);
10398};
10399
10400var forEachEventObj = function forEachEventObj(self, handler, events) {
10401 if (event(events)) {
10402 handler(self, events);
10403 return;
10404 } else if (plainObject(events)) {
10405 handler(self, makeEventObj(self, events));
10406 return;
10407 }
10408
10409 var eventList = array(events) ? events : events.split(/\s+/);
10410
10411 for (var i = 0; i < eventList.length; i++) {
10412 var evt = eventList[i];
10413
10414 if (emptyString(evt)) {
10415 continue;
10416 }
10417
10418 var match = evt.match(eventRegex); // type[.namespace]
10419
10420 if (match) {
10421 var type = match[1];
10422 var namespace = match[2] ? match[2] : null;
10423 var eventObj = makeEventObj(self, {
10424 type: type,
10425 namespace: namespace,
10426 target: self.context
10427 });
10428 handler(self, eventObj);
10429 }
10430 }
10431};
10432
10433p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
10434 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
10435 if (fn(callback)) {
10436 self.listeners.push({
10437 event: event,
10438 // full event string
10439 callback: callback,
10440 // callback to run
10441 type: type,
10442 // the event type (e.g. 'click')
10443 namespace: namespace,
10444 // the event namespace (e.g. ".foo")
10445 qualifier: qualifier,
10446 // a restriction on whether to match this emitter
10447 conf: conf // additional configuration
10448
10449 });
10450 }
10451 }, events, qualifier, callback, conf, confOverrides);
10452 return this;
10453};
10454
10455p.one = function (events, qualifier, callback, conf) {
10456 return this.on(events, qualifier, callback, conf, {
10457 one: true
10458 });
10459};
10460
10461p.removeListener = p.off = function (events, qualifier, callback, conf) {
10462 var _this = this;
10463
10464 if (this.emitting !== 0) {
10465 this.listeners = copyArray(this.listeners);
10466 }
10467
10468 var listeners = this.listeners;
10469
10470 var _loop = function _loop(i) {
10471 var listener = listeners[i];
10472 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
10473 /*, conf*/
10474 ) {
10475 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
10476 listeners.splice(i, 1);
10477 return false;
10478 }
10479 }, events, qualifier, callback, conf);
10480 };
10481
10482 for (var i = listeners.length - 1; i >= 0; i--) {
10483 _loop(i);
10484 }
10485
10486 return this;
10487};
10488
10489p.removeAllListeners = function () {
10490 return this.removeListener('*');
10491};
10492
10493p.emit = p.trigger = function (events, extraParams, manualCallback) {
10494 var listeners = this.listeners;
10495 var numListenersBeforeEmit = listeners.length;
10496 this.emitting++;
10497
10498 if (!array(extraParams)) {
10499 extraParams = [extraParams];
10500 }
10501
10502 forEachEventObj(this, function (self, eventObj) {
10503 if (manualCallback != null) {
10504 listeners = [{
10505 event: eventObj.event,
10506 type: eventObj.type,
10507 namespace: eventObj.namespace,
10508 callback: manualCallback
10509 }];
10510 numListenersBeforeEmit = listeners.length;
10511 }
10512
10513 var _loop2 = function _loop2(i) {
10514 var listener = listeners[i];
10515
10516 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
10517 var args = [eventObj];
10518
10519 if (extraParams != null) {
10520 push(args, extraParams);
10521 }
10522
10523 self.beforeEmit(self.context, listener, eventObj);
10524
10525 if (listener.conf && listener.conf.one) {
10526 self.listeners = self.listeners.filter(function (l) {
10527 return l !== listener;
10528 });
10529 }
10530
10531 var context = self.callbackContext(self.context, listener, eventObj);
10532 var ret = listener.callback.apply(context, args);
10533 self.afterEmit(self.context, listener, eventObj);
10534
10535 if (ret === false) {
10536 eventObj.stopPropagation();
10537 eventObj.preventDefault();
10538 }
10539 } // if listener matches
10540
10541 };
10542
10543 for (var i = 0; i < numListenersBeforeEmit; i++) {
10544 _loop2(i);
10545 } // for listener
10546
10547
10548 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
10549 self.parent(self.context).emit(eventObj, extraParams);
10550 }
10551 }, events);
10552 this.emitting--;
10553 return this;
10554};
10555
10556var emitterOptions = {
10557 qualifierCompare: function qualifierCompare(selector1, selector2) {
10558 if (selector1 == null || selector2 == null) {
10559 return selector1 == null && selector2 == null;
10560 } else {
10561 return selector1.sameText(selector2);
10562 }
10563 },
10564 eventMatches: function eventMatches(ele, listener, eventObj) {
10565 var selector = listener.qualifier;
10566
10567 if (selector != null) {
10568 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
10569 }
10570
10571 return true;
10572 },
10573 addEventFields: function addEventFields(ele, evt) {
10574 evt.cy = ele.cy();
10575 evt.target = ele;
10576 },
10577 callbackContext: function callbackContext(ele, listener, eventObj) {
10578 return listener.qualifier != null ? eventObj.target : ele;
10579 },
10580 beforeEmit: function beforeEmit(context, listener
10581 /*, eventObj*/
10582 ) {
10583 if (listener.conf && listener.conf.once) {
10584 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
10585 }
10586 },
10587 bubble: function bubble() {
10588 return true;
10589 },
10590 parent: function parent(ele) {
10591 return ele.isChild() ? ele.parent() : ele.cy();
10592 }
10593};
10594
10595var argSelector = function argSelector(arg) {
10596 if (string(arg)) {
10597 return new Selector(arg);
10598 } else {
10599 return arg;
10600 }
10601};
10602
10603var elesfn$m = {
10604 createEmitter: function createEmitter() {
10605 for (var i = 0; i < this.length; i++) {
10606 var ele = this[i];
10607 var _p = ele._private;
10608
10609 if (!_p.emitter) {
10610 _p.emitter = new Emitter(emitterOptions, ele);
10611 }
10612 }
10613
10614 return this;
10615 },
10616 emitter: function emitter() {
10617 return this._private.emitter;
10618 },
10619 on: function on(events, selector, callback) {
10620 var argSel = argSelector(selector);
10621
10622 for (var i = 0; i < this.length; i++) {
10623 var ele = this[i];
10624 ele.emitter().on(events, argSel, callback);
10625 }
10626
10627 return this;
10628 },
10629 removeListener: function removeListener(events, selector, callback) {
10630 var argSel = argSelector(selector);
10631
10632 for (var i = 0; i < this.length; i++) {
10633 var ele = this[i];
10634 ele.emitter().removeListener(events, argSel, callback);
10635 }
10636
10637 return this;
10638 },
10639 removeAllListeners: function removeAllListeners() {
10640 for (var i = 0; i < this.length; i++) {
10641 var ele = this[i];
10642 ele.emitter().removeAllListeners();
10643 }
10644
10645 return this;
10646 },
10647 one: function one(events, selector, callback) {
10648 var argSel = argSelector(selector);
10649
10650 for (var i = 0; i < this.length; i++) {
10651 var ele = this[i];
10652 ele.emitter().one(events, argSel, callback);
10653 }
10654
10655 return this;
10656 },
10657 once: function once(events, selector, callback) {
10658 var argSel = argSelector(selector);
10659
10660 for (var i = 0; i < this.length; i++) {
10661 var ele = this[i];
10662 ele.emitter().on(events, argSel, callback, {
10663 once: true,
10664 onceCollection: this
10665 });
10666 }
10667 },
10668 emit: function emit(events, extraParams) {
10669 for (var i = 0; i < this.length; i++) {
10670 var ele = this[i];
10671 ele.emitter().emit(events, extraParams);
10672 }
10673
10674 return this;
10675 },
10676 emitAndNotify: function emitAndNotify(event, extraParams) {
10677 // for internal use only
10678 if (this.length === 0) {
10679 return;
10680 } // empty collections don't need to notify anything
10681 // notify renderer
10682
10683
10684 this.cy().notify(event, this);
10685 this.emit(event, extraParams);
10686 return this;
10687 }
10688};
10689define$3.eventAliasesOn(elesfn$m);
10690
10691var elesfn$n = {
10692 nodes: function nodes(selector) {
10693 return this.filter(function (ele) {
10694 return ele.isNode();
10695 }).filter(selector);
10696 },
10697 edges: function edges(selector) {
10698 return this.filter(function (ele) {
10699 return ele.isEdge();
10700 }).filter(selector);
10701 },
10702 // internal helper to get nodes and edges as separate collections with single iteration over elements
10703 byGroup: function byGroup() {
10704 var nodes = this.spawn();
10705 var edges = this.spawn();
10706
10707 for (var i = 0; i < this.length; i++) {
10708 var ele = this[i];
10709
10710 if (ele.isNode()) {
10711 nodes.merge(ele);
10712 } else {
10713 edges.merge(ele);
10714 }
10715 }
10716
10717 return {
10718 nodes: nodes,
10719 edges: edges
10720 };
10721 },
10722 filter: function filter(_filter, thisArg) {
10723 if (_filter === undefined) {
10724 // check this first b/c it's the most common/performant case
10725 return this;
10726 } else if (string(_filter) || elementOrCollection(_filter)) {
10727 return new Selector(_filter).filter(this);
10728 } else if (fn(_filter)) {
10729 var filterEles = this.spawn();
10730 var eles = this;
10731
10732 for (var i = 0; i < eles.length; i++) {
10733 var ele = eles[i];
10734 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
10735
10736 if (include) {
10737 filterEles.merge(ele);
10738 }
10739 }
10740
10741 return filterEles;
10742 }
10743
10744 return this.spawn(); // if not handled by above, give 'em an empty collection
10745 },
10746 not: function not(toRemove) {
10747 if (!toRemove) {
10748 return this;
10749 } else {
10750 if (string(toRemove)) {
10751 toRemove = this.filter(toRemove);
10752 }
10753
10754 var elements = [];
10755 var rMap = toRemove._private.map;
10756
10757 for (var i = 0; i < this.length; i++) {
10758 var element = this[i];
10759 var remove = rMap.has(element.id());
10760
10761 if (!remove) {
10762 elements.push(element);
10763 }
10764 }
10765
10766 return this.spawn(elements);
10767 }
10768 },
10769 absoluteComplement: function absoluteComplement() {
10770 var cy = this.cy();
10771 return cy.mutableElements().not(this);
10772 },
10773 intersect: function intersect(other) {
10774 // if a selector is specified, then filter by it instead
10775 if (string(other)) {
10776 var selector = other;
10777 return this.filter(selector);
10778 }
10779
10780 var elements = [];
10781 var col1 = this;
10782 var col2 = other;
10783 var col1Smaller = this.length < other.length;
10784 var map2 = col1Smaller ? col2._private.map : col1._private.map;
10785 var col = col1Smaller ? col1 : col2;
10786
10787 for (var i = 0; i < col.length; i++) {
10788 var id = col[i]._private.data.id;
10789 var entry = map2.get(id);
10790
10791 if (entry) {
10792 elements.push(entry.ele);
10793 }
10794 }
10795
10796 return this.spawn(elements);
10797 },
10798 xor: function xor(other) {
10799 var cy = this._private.cy;
10800
10801 if (string(other)) {
10802 other = cy.$(other);
10803 }
10804
10805 var elements = [];
10806 var col1 = this;
10807 var col2 = other;
10808
10809 var add = function add(col, other) {
10810 for (var i = 0; i < col.length; i++) {
10811 var ele = col[i];
10812 var id = ele._private.data.id;
10813 var inOther = other.hasElementWithId(id);
10814
10815 if (!inOther) {
10816 elements.push(ele);
10817 }
10818 }
10819 };
10820
10821 add(col1, col2);
10822 add(col2, col1);
10823 return this.spawn(elements);
10824 },
10825 diff: function diff(other) {
10826 var cy = this._private.cy;
10827
10828 if (string(other)) {
10829 other = cy.$(other);
10830 }
10831
10832 var left = [];
10833 var right = [];
10834 var both = [];
10835 var col1 = this;
10836 var col2 = other;
10837
10838 var add = function add(col, other, retEles) {
10839 for (var i = 0; i < col.length; i++) {
10840 var ele = col[i];
10841 var id = ele._private.data.id;
10842 var inOther = other.hasElementWithId(id);
10843
10844 if (inOther) {
10845 both.push(ele);
10846 } else {
10847 retEles.push(ele);
10848 }
10849 }
10850 };
10851
10852 add(col1, col2, left);
10853 add(col2, col1, right);
10854 return {
10855 left: this.spawn(left, {
10856 unique: true
10857 }),
10858 right: this.spawn(right, {
10859 unique: true
10860 }),
10861 both: this.spawn(both, {
10862 unique: true
10863 })
10864 };
10865 },
10866 add: function add(toAdd) {
10867 var cy = this._private.cy;
10868
10869 if (!toAdd) {
10870 return this;
10871 }
10872
10873 if (string(toAdd)) {
10874 var selector = toAdd;
10875 toAdd = cy.mutableElements().filter(selector);
10876 }
10877
10878 var elements = [];
10879
10880 for (var i = 0; i < this.length; i++) {
10881 elements.push(this[i]);
10882 }
10883
10884 var map = this._private.map;
10885
10886 for (var _i = 0; _i < toAdd.length; _i++) {
10887 var add = !map.has(toAdd[_i].id());
10888
10889 if (add) {
10890 elements.push(toAdd[_i]);
10891 }
10892 }
10893
10894 return this.spawn(elements);
10895 },
10896 // in place merge on calling collection
10897 merge: function merge(toAdd) {
10898 var _p = this._private;
10899 var cy = _p.cy;
10900
10901 if (!toAdd) {
10902 return this;
10903 }
10904
10905 if (toAdd && string(toAdd)) {
10906 var selector = toAdd;
10907 toAdd = cy.mutableElements().filter(selector);
10908 }
10909
10910 var map = _p.map;
10911
10912 for (var i = 0; i < toAdd.length; i++) {
10913 var toAddEle = toAdd[i];
10914 var id = toAddEle._private.data.id;
10915 var add = !map.has(id);
10916
10917 if (add) {
10918 var index = this.length++;
10919 this[index] = toAddEle;
10920 map.set(id, {
10921 ele: toAddEle,
10922 index: index
10923 });
10924 } else {
10925 // replace
10926 var _index = map.get(id).index;
10927 this[_index] = toAddEle;
10928 map.set(id, {
10929 ele: toAddEle,
10930 index: _index
10931 });
10932 }
10933 }
10934
10935 return this; // chaining
10936 },
10937 unmergeAt: function unmergeAt(i) {
10938 var ele = this[i];
10939 var id = ele.id();
10940 var _p = this._private;
10941 var map = _p.map; // remove ele
10942
10943 this[i] = undefined;
10944 map["delete"](id);
10945 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
10946
10947 if (this.length > 1 && !unmergedLastEle) {
10948 var lastEleI = this.length - 1;
10949 var lastEle = this[lastEleI];
10950 var lastEleId = lastEle._private.data.id;
10951 this[lastEleI] = undefined;
10952 this[i] = lastEle;
10953 map.set(lastEleId, {
10954 ele: lastEle,
10955 index: i
10956 });
10957 } // the collection is now 1 ele smaller
10958
10959
10960 this.length--;
10961 return this;
10962 },
10963 // remove single ele in place in calling collection
10964 unmergeOne: function unmergeOne(ele) {
10965 ele = ele[0];
10966 var _p = this._private;
10967 var id = ele._private.data.id;
10968 var map = _p.map;
10969 var entry = map.get(id);
10970
10971 if (!entry) {
10972 return this; // no need to remove
10973 }
10974
10975 var i = entry.index;
10976 this.unmergeAt(i);
10977 return this;
10978 },
10979 // remove eles in place on calling collection
10980 unmerge: function unmerge(toRemove) {
10981 var cy = this._private.cy;
10982
10983 if (!toRemove) {
10984 return this;
10985 }
10986
10987 if (toRemove && string(toRemove)) {
10988 var selector = toRemove;
10989 toRemove = cy.mutableElements().filter(selector);
10990 }
10991
10992 for (var i = 0; i < toRemove.length; i++) {
10993 this.unmergeOne(toRemove[i]);
10994 }
10995
10996 return this; // chaining
10997 },
10998 unmergeBy: function unmergeBy(toRmFn) {
10999 for (var i = this.length - 1; i >= 0; i--) {
11000 var ele = this[i];
11001
11002 if (toRmFn(ele)) {
11003 this.unmergeAt(i);
11004 }
11005 }
11006
11007 return this;
11008 },
11009 map: function map(mapFn, thisArg) {
11010 var arr = [];
11011 var eles = this;
11012
11013 for (var i = 0; i < eles.length; i++) {
11014 var ele = eles[i];
11015 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11016 arr.push(ret);
11017 }
11018
11019 return arr;
11020 },
11021 reduce: function reduce(fn, initialValue) {
11022 var val = initialValue;
11023 var eles = this;
11024
11025 for (var i = 0; i < eles.length; i++) {
11026 val = fn(val, eles[i], i, eles);
11027 }
11028
11029 return val;
11030 },
11031 max: function max(valFn, thisArg) {
11032 var max = -Infinity;
11033 var maxEle;
11034 var eles = this;
11035
11036 for (var i = 0; i < eles.length; i++) {
11037 var ele = eles[i];
11038 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11039
11040 if (val > max) {
11041 max = val;
11042 maxEle = ele;
11043 }
11044 }
11045
11046 return {
11047 value: max,
11048 ele: maxEle
11049 };
11050 },
11051 min: function min(valFn, thisArg) {
11052 var min = Infinity;
11053 var minEle;
11054 var eles = this;
11055
11056 for (var i = 0; i < eles.length; i++) {
11057 var ele = eles[i];
11058 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11059
11060 if (val < min) {
11061 min = val;
11062 minEle = ele;
11063 }
11064 }
11065
11066 return {
11067 value: min,
11068 ele: minEle
11069 };
11070 }
11071}; // aliases
11072
11073var fn$5 = elesfn$n;
11074fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
11075fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
11076fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
11077fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
11078fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
11079fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
11080
11081var elesfn$o = {
11082 isNode: function isNode() {
11083 return this.group() === 'nodes';
11084 },
11085 isEdge: function isEdge() {
11086 return this.group() === 'edges';
11087 },
11088 isLoop: function isLoop() {
11089 return this.isEdge() && this.source()[0] === this.target()[0];
11090 },
11091 isSimple: function isSimple() {
11092 return this.isEdge() && this.source()[0] !== this.target()[0];
11093 },
11094 group: function group() {
11095 var ele = this[0];
11096
11097 if (ele) {
11098 return ele._private.group;
11099 }
11100 }
11101};
11102
11103/**
11104 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11105 * and z-index (low to high). These styles affect how this applies:
11106 *
11107 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11108 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11109 * root to leaves of the compound graph. The last drawn is `top`.
11110 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11111 * `manual` ignores this convention and draws based on the `z-index` value setting.
11112 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11113 * `z-index` will be drawn on top of an element with a lower `z-index`.
11114 */
11115
11116var zIndexSort = function zIndexSort(a, b) {
11117 var cy = a.cy();
11118 var hasCompoundNodes = cy.hasCompoundNodes();
11119
11120 function getDepth(ele) {
11121 var style = ele.pstyle('z-compound-depth');
11122
11123 if (style.value === 'auto') {
11124 return hasCompoundNodes ? ele.zDepth() : 0;
11125 } else if (style.value === 'bottom') {
11126 return -1;
11127 } else if (style.value === 'top') {
11128 return MAX_INT;
11129 } // 'orphan'
11130
11131
11132 return 0;
11133 }
11134
11135 var depthDiff = getDepth(a) - getDepth(b);
11136
11137 if (depthDiff !== 0) {
11138 return depthDiff;
11139 }
11140
11141 function getEleDepth(ele) {
11142 var style = ele.pstyle('z-index-compare');
11143
11144 if (style.value === 'auto') {
11145 return ele.isNode() ? 1 : 0;
11146 } // 'manual'
11147
11148
11149 return 0;
11150 }
11151
11152 var eleDiff = getEleDepth(a) - getEleDepth(b);
11153
11154 if (eleDiff !== 0) {
11155 return eleDiff;
11156 }
11157
11158 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11159
11160 if (zDiff !== 0) {
11161 return zDiff;
11162 } // compare indices in the core (order added to graph w/ last on top)
11163
11164
11165 return a.poolIndex() - b.poolIndex();
11166};
11167
11168var elesfn$p = {
11169 forEach: function forEach(fn$1, thisArg) {
11170 if (fn(fn$1)) {
11171 var N = this.length;
11172
11173 for (var i = 0; i < N; i++) {
11174 var ele = this[i];
11175 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
11176
11177 if (ret === false) {
11178 break;
11179 } // exit each early on return false
11180
11181 }
11182 }
11183
11184 return this;
11185 },
11186 toArray: function toArray() {
11187 var array = [];
11188
11189 for (var i = 0; i < this.length; i++) {
11190 array.push(this[i]);
11191 }
11192
11193 return array;
11194 },
11195 slice: function slice(start, end) {
11196 var array = [];
11197 var thisSize = this.length;
11198
11199 if (end == null) {
11200 end = thisSize;
11201 }
11202
11203 if (start == null) {
11204 start = 0;
11205 }
11206
11207 if (start < 0) {
11208 start = thisSize + start;
11209 }
11210
11211 if (end < 0) {
11212 end = thisSize + end;
11213 }
11214
11215 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11216 array.push(this[i]);
11217 }
11218
11219 return this.spawn(array);
11220 },
11221 size: function size() {
11222 return this.length;
11223 },
11224 eq: function eq(i) {
11225 return this[i] || this.spawn();
11226 },
11227 first: function first() {
11228 return this[0] || this.spawn();
11229 },
11230 last: function last() {
11231 return this[this.length - 1] || this.spawn();
11232 },
11233 empty: function empty() {
11234 return this.length === 0;
11235 },
11236 nonempty: function nonempty() {
11237 return !this.empty();
11238 },
11239 sort: function sort(sortFn) {
11240 if (!fn(sortFn)) {
11241 return this;
11242 }
11243
11244 var sorted = this.toArray().sort(sortFn);
11245 return this.spawn(sorted);
11246 },
11247 sortByZIndex: function sortByZIndex() {
11248 return this.sort(zIndexSort);
11249 },
11250 zDepth: function zDepth() {
11251 var ele = this[0];
11252
11253 if (!ele) {
11254 return undefined;
11255 } // let cy = ele.cy();
11256
11257
11258 var _p = ele._private;
11259 var group = _p.group;
11260
11261 if (group === 'nodes') {
11262 var depth = _p.data.parent ? ele.parents().size() : 0;
11263
11264 if (!ele.isParent()) {
11265 return MAX_INT - 1; // childless nodes always on top
11266 }
11267
11268 return depth;
11269 } else {
11270 var src = _p.source;
11271 var tgt = _p.target;
11272 var srcDepth = src.zDepth();
11273 var tgtDepth = tgt.zDepth();
11274 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11275 }
11276 }
11277};
11278elesfn$p.each = elesfn$p.forEach;
11279
11280var defineSymbolIterator = function defineSymbolIterator() {
11281 var typeofUndef = "undefined" ;
11282 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
11283
11284 if (isIteratorSupported) {
11285 elesfn$p[Symbol.iterator] = function () {
11286 var _this = this;
11287
11288 // eslint-disable-line no-undef
11289 var entry = {
11290 value: undefined,
11291 done: false
11292 };
11293 var i = 0;
11294 var length = this.length;
11295 return _defineProperty({
11296 next: function next() {
11297 if (i < length) {
11298 entry.value = _this[i++];
11299 } else {
11300 entry.value = undefined;
11301 entry.done = true;
11302 }
11303
11304 return entry;
11305 }
11306 }, Symbol.iterator, function () {
11307 // eslint-disable-line no-undef
11308 return this;
11309 });
11310 };
11311 }
11312};
11313
11314defineSymbolIterator();
11315
11316var getLayoutDimensionOptions = defaults({
11317 nodeDimensionsIncludeLabels: false
11318});
11319var elesfn$q = {
11320 // Calculates and returns node dimensions { x, y } based on options given
11321 layoutDimensions: function layoutDimensions(options) {
11322 options = getLayoutDimensionOptions(options);
11323 var dims;
11324
11325 if (!this.takesUpSpace()) {
11326 dims = {
11327 w: 0,
11328 h: 0
11329 };
11330 } else if (options.nodeDimensionsIncludeLabels) {
11331 var bbDim = this.boundingBox();
11332 dims = {
11333 w: bbDim.w,
11334 h: bbDim.h
11335 };
11336 } else {
11337 dims = {
11338 w: this.outerWidth(),
11339 h: this.outerHeight()
11340 };
11341 } // sanitise the dimensions for external layouts (avoid division by zero)
11342
11343
11344 if (dims.w === 0 || dims.h === 0) {
11345 dims.w = dims.h = 1;
11346 }
11347
11348 return dims;
11349 },
11350 // using standard layout options, apply position function (w/ or w/o animation)
11351 layoutPositions: function layoutPositions(layout, options, fn) {
11352 var nodes = this.nodes();
11353 var cy = this.cy();
11354 var layoutEles = options.eles; // nodes & edges
11355
11356 var getMemoizeKey = function getMemoizeKey(node) {
11357 return node.id();
11358 };
11359
11360 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
11361
11362 layout.emit({
11363 type: 'layoutstart',
11364 layout: layout
11365 });
11366 layout.animations = [];
11367
11368 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
11369 var center = {
11370 x: nodesBb.x1 + nodesBb.w / 2,
11371 y: nodesBb.y1 + nodesBb.h / 2
11372 };
11373 var spacingVector = {
11374 // scale from center of bounding box (not necessarily 0,0)
11375 x: (pos.x - center.x) * spacing,
11376 y: (pos.y - center.y) * spacing
11377 };
11378 return {
11379 x: center.x + spacingVector.x,
11380 y: center.y + spacingVector.y
11381 };
11382 };
11383
11384 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
11385
11386 var spacingBb = function spacingBb() {
11387 if (!useSpacingFactor) {
11388 return null;
11389 }
11390
11391 var bb = makeBoundingBox();
11392
11393 for (var i = 0; i < nodes.length; i++) {
11394 var node = nodes[i];
11395 var pos = fnMem(node, i);
11396 expandBoundingBoxByPoint(bb, pos.x, pos.y);
11397 }
11398
11399 return bb;
11400 };
11401
11402 var bb = spacingBb();
11403 var getFinalPos = memoize(function (node, i) {
11404 var newPos = fnMem(node, i);
11405
11406 if (useSpacingFactor) {
11407 var spacing = Math.abs(options.spacingFactor);
11408 newPos = calculateSpacing(spacing, bb, newPos);
11409 }
11410
11411 if (options.transform != null) {
11412 newPos = options.transform(node, newPos);
11413 }
11414
11415 return newPos;
11416 }, getMemoizeKey);
11417
11418 if (options.animate) {
11419 for (var i = 0; i < nodes.length; i++) {
11420 var node = nodes[i];
11421 var newPos = getFinalPos(node, i);
11422 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
11423
11424 if (animateNode) {
11425 var ani = node.animation({
11426 position: newPos,
11427 duration: options.animationDuration,
11428 easing: options.animationEasing
11429 });
11430 layout.animations.push(ani);
11431 } else {
11432 node.position(newPos);
11433 }
11434 }
11435
11436 if (options.fit) {
11437 var fitAni = cy.animation({
11438 fit: {
11439 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
11440 padding: options.padding
11441 },
11442 duration: options.animationDuration,
11443 easing: options.animationEasing
11444 });
11445 layout.animations.push(fitAni);
11446 } else if (options.zoom !== undefined && options.pan !== undefined) {
11447 var zoomPanAni = cy.animation({
11448 zoom: options.zoom,
11449 pan: options.pan,
11450 duration: options.animationDuration,
11451 easing: options.animationEasing
11452 });
11453 layout.animations.push(zoomPanAni);
11454 }
11455
11456 layout.animations.forEach(function (ani) {
11457 return ani.play();
11458 });
11459 layout.one('layoutready', options.ready);
11460 layout.emit({
11461 type: 'layoutready',
11462 layout: layout
11463 });
11464 Promise$1.all(layout.animations.map(function (ani) {
11465 return ani.promise();
11466 })).then(function () {
11467 layout.one('layoutstop', options.stop);
11468 layout.emit({
11469 type: 'layoutstop',
11470 layout: layout
11471 });
11472 });
11473 } else {
11474 nodes.positions(getFinalPos);
11475
11476 if (options.fit) {
11477 cy.fit(options.eles, options.padding);
11478 }
11479
11480 if (options.zoom != null) {
11481 cy.zoom(options.zoom);
11482 }
11483
11484 if (options.pan) {
11485 cy.pan(options.pan);
11486 }
11487
11488 layout.one('layoutready', options.ready);
11489 layout.emit({
11490 type: 'layoutready',
11491 layout: layout
11492 });
11493 layout.one('layoutstop', options.stop);
11494 layout.emit({
11495 type: 'layoutstop',
11496 layout: layout
11497 });
11498 }
11499
11500 return this; // chaining
11501 },
11502 layout: function layout(options) {
11503 var cy = this.cy();
11504 return cy.makeLayout(extend({}, options, {
11505 eles: this
11506 }));
11507 }
11508}; // aliases:
11509
11510elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
11511
11512function styleCache(key, fn, ele) {
11513 var _p = ele._private;
11514 var cache = _p.styleCache = _p.styleCache || [];
11515 var val;
11516
11517 if ((val = cache[key]) != null) {
11518 return val;
11519 } else {
11520 val = cache[key] = fn(ele);
11521 return val;
11522 }
11523}
11524
11525function cacheStyleFunction(key, fn) {
11526 key = hashString(key);
11527 return function cachedStyleFunction(ele) {
11528 return styleCache(key, fn, ele);
11529 };
11530}
11531
11532function cachePrototypeStyleFunction(key, fn) {
11533 key = hashString(key);
11534
11535 var selfFn = function selfFn(ele) {
11536 return fn.call(ele);
11537 };
11538
11539 return function cachedPrototypeStyleFunction() {
11540 var ele = this[0];
11541
11542 if (ele) {
11543 return styleCache(key, selfFn, ele);
11544 }
11545 };
11546}
11547
11548var elesfn$r = {
11549 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
11550 var cy = this.cy();
11551 var renderer = cy.renderer();
11552 var styleEnabled = cy.styleEnabled();
11553
11554 if (renderer && styleEnabled) {
11555 renderer.recalculateRenderedStyle(this, useCache);
11556 }
11557
11558 return this;
11559 },
11560 dirtyStyleCache: function dirtyStyleCache() {
11561 var cy = this.cy();
11562
11563 var dirty = function dirty(ele) {
11564 return ele._private.styleCache = null;
11565 };
11566
11567 if (cy.hasCompoundNodes()) {
11568 var eles;
11569 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11570 eles.merge(eles.connectedEdges());
11571 eles.forEach(dirty);
11572 } else {
11573 this.forEach(function (ele) {
11574 dirty(ele);
11575 ele.connectedEdges().forEach(dirty);
11576 });
11577 }
11578
11579 return this;
11580 },
11581 // fully updates (recalculates) the style for the elements
11582 updateStyle: function updateStyle(notifyRenderer) {
11583 var cy = this._private.cy;
11584
11585 if (!cy.styleEnabled()) {
11586 return this;
11587 }
11588
11589 if (cy.batching()) {
11590 var bEles = cy._private.batchStyleEles;
11591 bEles.merge(this);
11592 return this; // chaining and exit early when batching
11593 }
11594
11595 var hasCompounds = cy.hasCompoundNodes();
11596 var style = cy.style();
11597 var updatedEles = this;
11598 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
11599
11600 if (hasCompounds) {
11601 // then add everything up and down for compound selector checks
11602 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11603 }
11604
11605 var changedEles = style.apply(updatedEles);
11606
11607 if (notifyRenderer) {
11608 changedEles.emitAndNotify('style'); // let renderer know we changed style
11609 } else {
11610 changedEles.emit('style'); // just fire the event
11611 }
11612
11613 return this; // chaining
11614 },
11615 // get the internal parsed style object for the specified property
11616 parsedStyle: function parsedStyle(property) {
11617 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11618 var ele = this[0];
11619 var cy = ele.cy();
11620
11621 if (!cy.styleEnabled()) {
11622 return;
11623 }
11624
11625 if (ele) {
11626 var overriddenStyle = ele._private.style[property];
11627
11628 if (overriddenStyle != null) {
11629 return overriddenStyle;
11630 } else if (includeNonDefault) {
11631 return cy.style().getDefaultProperty(property);
11632 } else {
11633 return null;
11634 }
11635 }
11636 },
11637 numericStyle: function numericStyle(property) {
11638 var ele = this[0];
11639
11640 if (!ele.cy().styleEnabled()) {
11641 return;
11642 }
11643
11644 if (ele) {
11645 var pstyle = ele.pstyle(property);
11646 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
11647 }
11648 },
11649 numericStyleUnits: function numericStyleUnits(property) {
11650 var ele = this[0];
11651
11652 if (!ele.cy().styleEnabled()) {
11653 return;
11654 }
11655
11656 if (ele) {
11657 return ele.pstyle(property).units;
11658 }
11659 },
11660 // get the specified css property as a rendered value (i.e. on-screen value)
11661 // or get the whole rendered style if no property specified (NB doesn't allow setting)
11662 renderedStyle: function renderedStyle(property) {
11663 var cy = this.cy();
11664
11665 if (!cy.styleEnabled()) {
11666 return this;
11667 }
11668
11669 var ele = this[0];
11670
11671 if (ele) {
11672 return cy.style().getRenderedStyle(ele, property);
11673 }
11674 },
11675 // read the calculated css style of the element or override the style (via a bypass)
11676 style: function style(name, value) {
11677 var cy = this.cy();
11678
11679 if (!cy.styleEnabled()) {
11680 return this;
11681 }
11682
11683 var updateTransitions = false;
11684 var style = cy.style();
11685
11686 if (plainObject(name)) {
11687 // then extend the bypass
11688 var props = name;
11689 style.applyBypass(this, props, updateTransitions);
11690 this.emitAndNotify('style'); // let the renderer know we've updated style
11691 } else if (string(name)) {
11692 if (value === undefined) {
11693 // then get the property from the style
11694 var ele = this[0];
11695
11696 if (ele) {
11697 return style.getStylePropertyValue(ele, name);
11698 } else {
11699 // empty collection => can't get any value
11700 return;
11701 }
11702 } else {
11703 // then set the bypass with the property value
11704 style.applyBypass(this, name, value, updateTransitions);
11705 this.emitAndNotify('style'); // let the renderer know we've updated style
11706 }
11707 } else if (name === undefined) {
11708 var _ele = this[0];
11709
11710 if (_ele) {
11711 return style.getRawStyle(_ele);
11712 } else {
11713 // empty collection => can't get any value
11714 return;
11715 }
11716 }
11717
11718 return this; // chaining
11719 },
11720 removeStyle: function removeStyle(names) {
11721 var cy = this.cy();
11722
11723 if (!cy.styleEnabled()) {
11724 return this;
11725 }
11726
11727 var updateTransitions = false;
11728 var style = cy.style();
11729 var eles = this;
11730
11731 if (names === undefined) {
11732 for (var i = 0; i < eles.length; i++) {
11733 var ele = eles[i];
11734 style.removeAllBypasses(ele, updateTransitions);
11735 }
11736 } else {
11737 names = names.split(/\s+/);
11738
11739 for (var _i = 0; _i < eles.length; _i++) {
11740 var _ele2 = eles[_i];
11741 style.removeBypasses(_ele2, names, updateTransitions);
11742 }
11743 }
11744
11745 this.emitAndNotify('style'); // let the renderer know we've updated style
11746
11747 return this; // chaining
11748 },
11749 show: function show() {
11750 this.css('display', 'element');
11751 return this; // chaining
11752 },
11753 hide: function hide() {
11754 this.css('display', 'none');
11755 return this; // chaining
11756 },
11757 effectiveOpacity: function effectiveOpacity() {
11758 var cy = this.cy();
11759
11760 if (!cy.styleEnabled()) {
11761 return 1;
11762 }
11763
11764 var hasCompoundNodes = cy.hasCompoundNodes();
11765 var ele = this[0];
11766
11767 if (ele) {
11768 var _p = ele._private;
11769 var parentOpacity = ele.pstyle('opacity').value;
11770
11771 if (!hasCompoundNodes) {
11772 return parentOpacity;
11773 }
11774
11775 var parents = !_p.data.parent ? null : ele.parents();
11776
11777 if (parents) {
11778 for (var i = 0; i < parents.length; i++) {
11779 var parent = parents[i];
11780 var opacity = parent.pstyle('opacity').value;
11781 parentOpacity = opacity * parentOpacity;
11782 }
11783 }
11784
11785 return parentOpacity;
11786 }
11787 },
11788 transparent: function transparent() {
11789 var cy = this.cy();
11790
11791 if (!cy.styleEnabled()) {
11792 return false;
11793 }
11794
11795 var ele = this[0];
11796 var hasCompoundNodes = ele.cy().hasCompoundNodes();
11797
11798 if (ele) {
11799 if (!hasCompoundNodes) {
11800 return ele.pstyle('opacity').value === 0;
11801 } else {
11802 return ele.effectiveOpacity() === 0;
11803 }
11804 }
11805 },
11806 backgrounding: function backgrounding() {
11807 var cy = this.cy();
11808
11809 if (!cy.styleEnabled()) {
11810 return false;
11811 }
11812
11813 var ele = this[0];
11814 return ele._private.backgrounding ? true : false;
11815 }
11816};
11817
11818function checkCompound(ele, parentOk) {
11819 var _p = ele._private;
11820 var parents = _p.data.parent ? ele.parents() : null;
11821
11822 if (parents) {
11823 for (var i = 0; i < parents.length; i++) {
11824 var parent = parents[i];
11825
11826 if (!parentOk(parent)) {
11827 return false;
11828 }
11829 }
11830 }
11831
11832 return true;
11833}
11834
11835function defineDerivedStateFunction(specs) {
11836 var ok = specs.ok;
11837 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
11838 var parentOk = specs.parentOk || specs.ok;
11839 return function () {
11840 var cy = this.cy();
11841
11842 if (!cy.styleEnabled()) {
11843 return true;
11844 }
11845
11846 var ele = this[0];
11847 var hasCompoundNodes = cy.hasCompoundNodes();
11848
11849 if (ele) {
11850 var _p = ele._private;
11851
11852 if (!ok(ele)) {
11853 return false;
11854 }
11855
11856 if (ele.isNode()) {
11857 return !hasCompoundNodes || checkCompound(ele, parentOk);
11858 } else {
11859 var src = _p.source;
11860 var tgt = _p.target;
11861 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
11862 }
11863 }
11864 };
11865}
11866
11867var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
11868 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
11869});
11870elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
11871 ok: eleTakesUpSpace
11872}));
11873var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
11874 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
11875});
11876var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
11877 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
11878});
11879elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
11880 ok: eleInteractive,
11881 parentOk: parentInteractive,
11882 edgeOkViaNode: eleTakesUpSpace
11883}));
11884
11885elesfn$r.noninteractive = function () {
11886 var ele = this[0];
11887
11888 if (ele) {
11889 return !ele.interactive();
11890 }
11891};
11892
11893var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
11894 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
11895});
11896var edgeVisibleViaNode = eleTakesUpSpace;
11897elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
11898 ok: eleVisible,
11899 edgeOkViaNode: edgeVisibleViaNode
11900}));
11901
11902elesfn$r.hidden = function () {
11903 var ele = this[0];
11904
11905 if (ele) {
11906 return !ele.visible();
11907 }
11908};
11909
11910elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
11911 if (!this.cy().styleEnabled()) {
11912 return false;
11913 }
11914
11915 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
11916});
11917elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
11918elesfn$r.renderedCss = elesfn$r.renderedStyle;
11919elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
11920elesfn$r.pstyle = elesfn$r.parsedStyle;
11921
11922var elesfn$s = {};
11923
11924function defineSwitchFunction(params) {
11925 return function () {
11926 var args = arguments;
11927 var changedEles = []; // e.g. cy.nodes().select( data, handler )
11928
11929 if (args.length === 2) {
11930 var data = args[0];
11931 var handler = args[1];
11932 this.on(params.event, data, handler);
11933 } // e.g. cy.nodes().select( handler )
11934 else if (args.length === 1 && fn(args[0])) {
11935 var _handler = args[0];
11936 this.on(params.event, _handler);
11937 } // e.g. cy.nodes().select()
11938 // e.g. (private) cy.nodes().select(['tapselect'])
11939 else if (args.length === 0 || args.length === 1 && array(args[0])) {
11940 var addlEvents = args.length === 1 ? args[0] : null;
11941
11942 for (var i = 0; i < this.length; i++) {
11943 var ele = this[i];
11944 var able = !params.ableField || ele._private[params.ableField];
11945 var changed = ele._private[params.field] != params.value;
11946
11947 if (params.overrideAble) {
11948 var overrideAble = params.overrideAble(ele);
11949
11950 if (overrideAble !== undefined) {
11951 able = overrideAble;
11952
11953 if (!overrideAble) {
11954 return this;
11955 } // to save cycles assume not able for all on override
11956
11957 }
11958 }
11959
11960 if (able) {
11961 ele._private[params.field] = params.value;
11962
11963 if (changed) {
11964 changedEles.push(ele);
11965 }
11966 }
11967 }
11968
11969 var changedColl = this.spawn(changedEles);
11970 changedColl.updateStyle(); // change of state => possible change of style
11971
11972 changedColl.emit(params.event);
11973
11974 if (addlEvents) {
11975 changedColl.emit(addlEvents);
11976 }
11977 }
11978
11979 return this;
11980 };
11981}
11982
11983function defineSwitchSet(params) {
11984 elesfn$s[params.field] = function () {
11985 var ele = this[0];
11986
11987 if (ele) {
11988 if (params.overrideField) {
11989 var val = params.overrideField(ele);
11990
11991 if (val !== undefined) {
11992 return val;
11993 }
11994 }
11995
11996 return ele._private[params.field];
11997 }
11998 };
11999
12000 elesfn$s[params.on] = defineSwitchFunction({
12001 event: params.on,
12002 field: params.field,
12003 ableField: params.ableField,
12004 overrideAble: params.overrideAble,
12005 value: true
12006 });
12007 elesfn$s[params.off] = defineSwitchFunction({
12008 event: params.off,
12009 field: params.field,
12010 ableField: params.ableField,
12011 overrideAble: params.overrideAble,
12012 value: false
12013 });
12014}
12015
12016defineSwitchSet({
12017 field: 'locked',
12018 overrideField: function overrideField(ele) {
12019 return ele.cy().autolock() ? true : undefined;
12020 },
12021 on: 'lock',
12022 off: 'unlock'
12023});
12024defineSwitchSet({
12025 field: 'grabbable',
12026 overrideField: function overrideField(ele) {
12027 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12028 },
12029 on: 'grabify',
12030 off: 'ungrabify'
12031});
12032defineSwitchSet({
12033 field: 'selected',
12034 ableField: 'selectable',
12035 overrideAble: function overrideAble(ele) {
12036 return ele.cy().autounselectify() ? false : undefined;
12037 },
12038 on: 'select',
12039 off: 'unselect'
12040});
12041defineSwitchSet({
12042 field: 'selectable',
12043 overrideField: function overrideField(ele) {
12044 return ele.cy().autounselectify() ? false : undefined;
12045 },
12046 on: 'selectify',
12047 off: 'unselectify'
12048});
12049elesfn$s.deselect = elesfn$s.unselect;
12050
12051elesfn$s.grabbed = function () {
12052 var ele = this[0];
12053
12054 if (ele) {
12055 return ele._private.grabbed;
12056 }
12057};
12058
12059defineSwitchSet({
12060 field: 'active',
12061 on: 'activate',
12062 off: 'unactivate'
12063});
12064defineSwitchSet({
12065 field: 'pannable',
12066 on: 'panify',
12067 off: 'unpanify'
12068});
12069
12070elesfn$s.inactive = function () {
12071 var ele = this[0];
12072
12073 if (ele) {
12074 return !ele._private.active;
12075 }
12076};
12077
12078var elesfn$t = {}; // DAG functions
12079////////////////
12080
12081var defineDagExtremity = function defineDagExtremity(params) {
12082 return function dagExtremityImpl(selector) {
12083 var eles = this;
12084 var ret = [];
12085
12086 for (var i = 0; i < eles.length; i++) {
12087 var ele = eles[i];
12088
12089 if (!ele.isNode()) {
12090 continue;
12091 }
12092
12093 var disqualified = false;
12094 var edges = ele.connectedEdges();
12095
12096 for (var j = 0; j < edges.length; j++) {
12097 var edge = edges[j];
12098 var src = edge.source();
12099 var tgt = edge.target();
12100
12101 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12102 disqualified = true;
12103 break;
12104 }
12105 }
12106
12107 if (!disqualified) {
12108 ret.push(ele);
12109 }
12110 }
12111
12112 return this.spawn(ret, {
12113 unique: true
12114 }).filter(selector);
12115 };
12116};
12117
12118var defineDagOneHop = function defineDagOneHop(params) {
12119 return function (selector) {
12120 var eles = this;
12121 var oEles = [];
12122
12123 for (var i = 0; i < eles.length; i++) {
12124 var ele = eles[i];
12125
12126 if (!ele.isNode()) {
12127 continue;
12128 }
12129
12130 var edges = ele.connectedEdges();
12131
12132 for (var j = 0; j < edges.length; j++) {
12133 var edge = edges[j];
12134 var src = edge.source();
12135 var tgt = edge.target();
12136
12137 if (params.outgoing && src === ele) {
12138 oEles.push(edge);
12139 oEles.push(tgt);
12140 } else if (params.incoming && tgt === ele) {
12141 oEles.push(edge);
12142 oEles.push(src);
12143 }
12144 }
12145 }
12146
12147 return this.spawn(oEles, {
12148 unique: true
12149 }).filter(selector);
12150 };
12151};
12152
12153var defineDagAllHops = function defineDagAllHops(params) {
12154 return function (selector) {
12155 var eles = this;
12156 var sEles = [];
12157 var sElesIds = {};
12158
12159 for (;;) {
12160 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12161
12162 if (next.length === 0) {
12163 break;
12164 } // done if none left
12165
12166
12167 var newNext = false;
12168
12169 for (var i = 0; i < next.length; i++) {
12170 var n = next[i];
12171 var nid = n.id();
12172
12173 if (!sElesIds[nid]) {
12174 sElesIds[nid] = true;
12175 sEles.push(n);
12176 newNext = true;
12177 }
12178 }
12179
12180 if (!newNext) {
12181 break;
12182 } // done if touched all outgoers already
12183
12184
12185 eles = next;
12186 }
12187
12188 return this.spawn(sEles, {
12189 unique: true
12190 }).filter(selector);
12191 };
12192};
12193
12194elesfn$t.clearTraversalCache = function () {
12195 for (var i = 0; i < this.length; i++) {
12196 this[i]._private.traversalCache = null;
12197 }
12198};
12199
12200extend(elesfn$t, {
12201 // get the root nodes in the DAG
12202 roots: defineDagExtremity({
12203 noIncomingEdges: true
12204 }),
12205 // get the leaf nodes in the DAG
12206 leaves: defineDagExtremity({
12207 noOutgoingEdges: true
12208 }),
12209 // normally called children in graph theory
12210 // these nodes =edges=> outgoing nodes
12211 outgoers: cache(defineDagOneHop({
12212 outgoing: true
12213 }), 'outgoers'),
12214 // aka DAG descendants
12215 successors: defineDagAllHops({
12216 outgoing: true
12217 }),
12218 // normally called parents in graph theory
12219 // these nodes <=edges= incoming nodes
12220 incomers: cache(defineDagOneHop({
12221 incoming: true
12222 }), 'incomers'),
12223 // aka DAG ancestors
12224 predecessors: defineDagAllHops({
12225 incoming: true
12226 })
12227}); // Neighbourhood functions
12228//////////////////////////
12229
12230extend(elesfn$t, {
12231 neighborhood: cache(function (selector) {
12232 var elements = [];
12233 var nodes = this.nodes();
12234
12235 for (var i = 0; i < nodes.length; i++) {
12236 // for all nodes
12237 var node = nodes[i];
12238 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12239
12240 for (var j = 0; j < connectedEdges.length; j++) {
12241 var edge = connectedEdges[j];
12242 var src = edge.source();
12243 var tgt = edge.target();
12244 var otherNode = node === src ? tgt : src; // need check in case of loop
12245
12246 if (otherNode.length > 0) {
12247 elements.push(otherNode[0]); // add node 1 hop away
12248 } // add connected edge
12249
12250
12251 elements.push(edge[0]);
12252 }
12253 }
12254
12255 return this.spawn(elements, {
12256 unique: true
12257 }).filter(selector);
12258 }, 'neighborhood'),
12259 closedNeighborhood: function closedNeighborhood(selector) {
12260 return this.neighborhood().add(this).filter(selector);
12261 },
12262 openNeighborhood: function openNeighborhood(selector) {
12263 return this.neighborhood(selector);
12264 }
12265}); // aliases
12266
12267elesfn$t.neighbourhood = elesfn$t.neighborhood;
12268elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
12269elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
12270/////////////////
12271
12272extend(elesfn$t, {
12273 source: cache(function sourceImpl(selector) {
12274 var ele = this[0];
12275 var src;
12276
12277 if (ele) {
12278 src = ele._private.source || ele.cy().collection();
12279 }
12280
12281 return src && selector ? src.filter(selector) : src;
12282 }, 'source'),
12283 target: cache(function targetImpl(selector) {
12284 var ele = this[0];
12285 var tgt;
12286
12287 if (ele) {
12288 tgt = ele._private.target || ele.cy().collection();
12289 }
12290
12291 return tgt && selector ? tgt.filter(selector) : tgt;
12292 }, 'target'),
12293 sources: defineSourceFunction({
12294 attr: 'source'
12295 }),
12296 targets: defineSourceFunction({
12297 attr: 'target'
12298 })
12299});
12300
12301function defineSourceFunction(params) {
12302 return function sourceImpl(selector) {
12303 var sources = [];
12304
12305 for (var i = 0; i < this.length; i++) {
12306 var ele = this[i];
12307 var src = ele._private[params.attr];
12308
12309 if (src) {
12310 sources.push(src);
12311 }
12312 }
12313
12314 return this.spawn(sources, {
12315 unique: true
12316 }).filter(selector);
12317 };
12318}
12319
12320extend(elesfn$t, {
12321 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
12322 edgesTo: cache(defineEdgesWithFunction({
12323 thisIsSrc: true
12324 }), 'edgesTo')
12325});
12326
12327function defineEdgesWithFunction(params) {
12328 return function edgesWithImpl(otherNodes) {
12329 var elements = [];
12330 var cy = this._private.cy;
12331 var p = params || {}; // get elements if a selector is specified
12332
12333 if (string(otherNodes)) {
12334 otherNodes = cy.$(otherNodes);
12335 }
12336
12337 for (var h = 0; h < otherNodes.length; h++) {
12338 var edges = otherNodes[h]._private.edges;
12339
12340 for (var i = 0; i < edges.length; i++) {
12341 var edge = edges[i];
12342 var edgeData = edge._private.data;
12343 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
12344 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
12345 var edgeConnectsThisAndOther = thisToOther || otherToThis;
12346
12347 if (!edgeConnectsThisAndOther) {
12348 continue;
12349 }
12350
12351 if (p.thisIsSrc || p.thisIsTgt) {
12352 if (p.thisIsSrc && !thisToOther) {
12353 continue;
12354 }
12355
12356 if (p.thisIsTgt && !otherToThis) {
12357 continue;
12358 }
12359 }
12360
12361 elements.push(edge);
12362 }
12363 }
12364
12365 return this.spawn(elements, {
12366 unique: true
12367 });
12368 };
12369}
12370
12371extend(elesfn$t, {
12372 connectedEdges: cache(function (selector) {
12373 var retEles = [];
12374 var eles = this;
12375
12376 for (var i = 0; i < eles.length; i++) {
12377 var node = eles[i];
12378
12379 if (!node.isNode()) {
12380 continue;
12381 }
12382
12383 var edges = node._private.edges;
12384
12385 for (var j = 0; j < edges.length; j++) {
12386 var edge = edges[j];
12387 retEles.push(edge);
12388 }
12389 }
12390
12391 return this.spawn(retEles, {
12392 unique: true
12393 }).filter(selector);
12394 }, 'connectedEdges'),
12395 connectedNodes: cache(function (selector) {
12396 var retEles = [];
12397 var eles = this;
12398
12399 for (var i = 0; i < eles.length; i++) {
12400 var edge = eles[i];
12401
12402 if (!edge.isEdge()) {
12403 continue;
12404 }
12405
12406 retEles.push(edge.source()[0]);
12407 retEles.push(edge.target()[0]);
12408 }
12409
12410 return this.spawn(retEles, {
12411 unique: true
12412 }).filter(selector);
12413 }, 'connectedNodes'),
12414 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
12415 codirectedEdges: cache(defineParallelEdgesFunction({
12416 codirected: true
12417 }), 'codirectedEdges')
12418});
12419
12420function defineParallelEdgesFunction(params) {
12421 var defaults = {
12422 codirected: false
12423 };
12424 params = extend({}, defaults, params);
12425 return function parallelEdgesImpl(selector) {
12426 // micro-optimised for renderer
12427 var elements = [];
12428 var edges = this.edges();
12429 var p = params; // look at all the edges in the collection
12430
12431 for (var i = 0; i < edges.length; i++) {
12432 var edge1 = edges[i];
12433 var edge1_p = edge1._private;
12434 var src1 = edge1_p.source;
12435 var srcid1 = src1._private.data.id;
12436 var tgtid1 = edge1_p.data.target;
12437 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
12438
12439 for (var j = 0; j < srcEdges1.length; j++) {
12440 var edge2 = srcEdges1[j];
12441 var edge2data = edge2._private.data;
12442 var tgtid2 = edge2data.target;
12443 var srcid2 = edge2data.source;
12444 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
12445 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
12446
12447 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
12448 elements.push(edge2);
12449 }
12450 }
12451 }
12452
12453 return this.spawn(elements, {
12454 unique: true
12455 }).filter(selector);
12456 };
12457} // Misc functions
12458/////////////////
12459
12460
12461extend(elesfn$t, {
12462 components: function components(root) {
12463 var self = this;
12464 var cy = self.cy();
12465 var visited = cy.collection();
12466 var unvisited = root == null ? self.nodes() : root.nodes();
12467 var components = [];
12468
12469 if (root != null && unvisited.empty()) {
12470 // root may contain only edges
12471 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
12472 }
12473
12474 var visitInComponent = function visitInComponent(node, component) {
12475 visited.merge(node);
12476 unvisited.unmerge(node);
12477 component.merge(node);
12478 };
12479
12480 if (unvisited.empty()) {
12481 return self.spawn();
12482 }
12483
12484 var _loop = function _loop() {
12485 // each iteration yields a component
12486 var cmpt = cy.collection();
12487 components.push(cmpt);
12488 var root = unvisited[0];
12489 visitInComponent(root, cmpt);
12490 self.bfs({
12491 directed: false,
12492 roots: root,
12493 visit: function visit(v) {
12494 return visitInComponent(v, cmpt);
12495 }
12496 });
12497 cmpt.forEach(function (node) {
12498 node.connectedEdges().forEach(function (e) {
12499 // connectedEdges() usually cached
12500 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
12501 // has() is cheap
12502 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
12503 }
12504 });
12505 });
12506 };
12507
12508 do {
12509 _loop();
12510 } while (unvisited.length > 0);
12511
12512 return components;
12513 },
12514 component: function component() {
12515 var ele = this[0];
12516 return ele.cy().mutableElements().components(ele)[0];
12517 }
12518});
12519elesfn$t.componentsOf = elesfn$t.components;
12520
12521var idFactory = {
12522 generate: function generate(cy, element, tryThisId) {
12523 var id = tryThisId != null ? tryThisId : uuid();
12524
12525 while (cy.hasElementWithId(id)) {
12526 id = uuid();
12527 }
12528
12529 return id;
12530 }
12531}; // represents a set of nodes, edges, or both together
12532
12533var Collection = function Collection(cy, elements, options) {
12534 if (cy === undefined || !core(cy)) {
12535 error('A collection must have a reference to the core');
12536 return;
12537 }
12538
12539 var map = new Map$1();
12540 var createdElements = false;
12541
12542 if (!elements) {
12543 elements = [];
12544 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
12545 createdElements = true; // make elements from json and restore all at once later
12546
12547 var eles = [];
12548 var elesIds = new Set$1();
12549
12550 for (var i = 0, l = elements.length; i < l; i++) {
12551 var json = elements[i];
12552
12553 if (json.data == null) {
12554 json.data = {};
12555 }
12556
12557 var _data = json.data; // make sure newly created elements have valid ids
12558
12559 if (_data.id == null) {
12560 _data.id = idFactory.generate(cy, json);
12561 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
12562 continue; // can't create element if prior id already exists
12563 }
12564
12565 var ele = new Element(cy, json, false);
12566 eles.push(ele);
12567 elesIds.add(_data.id);
12568 }
12569
12570 elements = eles;
12571 }
12572
12573 this.length = 0;
12574
12575 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
12576 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
12577
12578 if (element$1 == null) {
12579 continue;
12580 }
12581
12582 var id = element$1._private.data.id;
12583
12584 if (options == null || options.unique && !map.has(id)) {
12585 map.set(id, {
12586 index: this.length,
12587 ele: element$1
12588 });
12589 this[this.length] = element$1;
12590 this.length++;
12591 }
12592 }
12593
12594 this._private = {
12595 cy: cy,
12596 map: map
12597 }; // restore the elements if we created them from json
12598
12599 if (createdElements) {
12600 this.restore();
12601 }
12602}; // Functions
12603////////////////////////////////////////////////////////////////////////////////////////////////////
12604// keep the prototypes in sync (an element has the same functions as a collection)
12605// and use elefn and elesfn as shorthands to the prototypes
12606
12607
12608var elesfn$u = Element.prototype = Collection.prototype;
12609
12610elesfn$u.instanceString = function () {
12611 return 'collection';
12612};
12613
12614elesfn$u.spawn = function (cy, eles, opts) {
12615 if (!core(cy)) {
12616 // cy is optional
12617 opts = eles;
12618 eles = cy;
12619 cy = this.cy();
12620 }
12621
12622 return new Collection(cy, eles, opts);
12623};
12624
12625elesfn$u.spawnSelf = function () {
12626 return this.spawn(this);
12627};
12628
12629elesfn$u.cy = function () {
12630 return this._private.cy;
12631};
12632
12633elesfn$u.renderer = function () {
12634 return this._private.cy.renderer();
12635};
12636
12637elesfn$u.element = function () {
12638 return this[0];
12639};
12640
12641elesfn$u.collection = function () {
12642 if (collection(this)) {
12643 return this;
12644 } else {
12645 // an element
12646 return new Collection(this._private.cy, [this]);
12647 }
12648};
12649
12650elesfn$u.unique = function () {
12651 return new Collection(this._private.cy, this, {
12652 unique: true
12653 });
12654};
12655
12656elesfn$u.hasElementWithId = function (id) {
12657 id = '' + id; // id must be string
12658
12659 return this._private.map.has(id);
12660};
12661
12662elesfn$u.getElementById = function (id) {
12663 id = '' + id; // id must be string
12664
12665 var cy = this._private.cy;
12666
12667 var entry = this._private.map.get(id);
12668
12669 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
12670};
12671
12672elesfn$u.$id = elesfn$u.getElementById;
12673
12674elesfn$u.poolIndex = function () {
12675 var cy = this._private.cy;
12676 var eles = cy._private.elements;
12677 var id = this[0]._private.data.id;
12678 return eles._private.map.get(id).index;
12679};
12680
12681elesfn$u.indexOf = function (ele) {
12682 var id = ele[0]._private.data.id;
12683 return this._private.map.get(id).index;
12684};
12685
12686elesfn$u.indexOfId = function (id) {
12687 id = '' + id; // id must be string
12688
12689 return this._private.map.get(id).index;
12690};
12691
12692elesfn$u.json = function (obj) {
12693 var ele = this.element();
12694 var cy = this.cy();
12695
12696 if (ele == null && obj) {
12697 return this;
12698 } // can't set to no eles
12699
12700
12701 if (ele == null) {
12702 return undefined;
12703 } // can't get from no eles
12704
12705
12706 var p = ele._private;
12707
12708 if (plainObject(obj)) {
12709 // set
12710 cy.startBatch();
12711
12712 if (obj.data) {
12713 ele.data(obj.data);
12714 var _data2 = p.data;
12715
12716 if (ele.isEdge()) {
12717 // source and target are immutable via data()
12718 var move = false;
12719 var spec = {};
12720 var src = obj.data.source;
12721 var tgt = obj.data.target;
12722
12723 if (src != null && src != _data2.source) {
12724 spec.source = '' + src; // id must be string
12725
12726 move = true;
12727 }
12728
12729 if (tgt != null && tgt != _data2.target) {
12730 spec.target = '' + tgt; // id must be string
12731
12732 move = true;
12733 }
12734
12735 if (move) {
12736 ele = ele.move(spec);
12737 }
12738 } else {
12739 // parent is immutable via data()
12740 var parent = obj.data.parent;
12741
12742 if ((parent != null || _data2.parent != null) && parent != _data2.parent) {
12743 if (parent === undefined) {
12744 // can't set undefined imperatively, so use null
12745 parent = null;
12746 }
12747
12748 if (parent != null) {
12749 parent = '' + parent; // id must be string
12750 }
12751
12752 ele = ele.move({
12753 parent: parent
12754 });
12755 }
12756 }
12757 }
12758
12759 if (obj.position) {
12760 ele.position(obj.position);
12761 } // ignore group -- immutable
12762
12763
12764 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
12765 var obj_k = obj[k];
12766
12767 if (obj_k != null && obj_k !== p[k]) {
12768 if (obj_k) {
12769 ele[trueFnName]();
12770 } else {
12771 ele[falseFnName]();
12772 }
12773 }
12774 };
12775
12776 checkSwitch('removed', 'remove', 'restore');
12777 checkSwitch('selected', 'select', 'unselect');
12778 checkSwitch('selectable', 'selectify', 'unselectify');
12779 checkSwitch('locked', 'lock', 'unlock');
12780 checkSwitch('grabbable', 'grabify', 'ungrabify');
12781 checkSwitch('pannable', 'panify', 'unpanify');
12782
12783 if (obj.classes != null) {
12784 ele.classes(obj.classes);
12785 }
12786
12787 cy.endBatch();
12788 return this;
12789 } else if (obj === undefined) {
12790 // get
12791 var json = {
12792 data: copy(p.data),
12793 position: copy(p.position),
12794 group: p.group,
12795 removed: p.removed,
12796 selected: p.selected,
12797 selectable: p.selectable,
12798 locked: p.locked,
12799 grabbable: p.grabbable,
12800 pannable: p.pannable,
12801 classes: null
12802 };
12803 json.classes = '';
12804 var i = 0;
12805 p.classes.forEach(function (cls) {
12806 return json.classes += i++ === 0 ? cls : ' ' + cls;
12807 });
12808 return json;
12809 }
12810};
12811
12812elesfn$u.jsons = function () {
12813 var jsons = [];
12814
12815 for (var i = 0; i < this.length; i++) {
12816 var ele = this[i];
12817 var json = ele.json();
12818 jsons.push(json);
12819 }
12820
12821 return jsons;
12822};
12823
12824elesfn$u.clone = function () {
12825 var cy = this.cy();
12826 var elesArr = [];
12827
12828 for (var i = 0; i < this.length; i++) {
12829 var ele = this[i];
12830 var json = ele.json();
12831 var clone = new Element(cy, json, false); // NB no restore
12832
12833 elesArr.push(clone);
12834 }
12835
12836 return new Collection(cy, elesArr);
12837};
12838
12839elesfn$u.copy = elesfn$u.clone;
12840
12841elesfn$u.restore = function () {
12842 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12843 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12844 var self = this;
12845 var cy = self.cy();
12846 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
12847 // restore the nodes first
12848
12849 var nodes = [];
12850 var edges = [];
12851 var elements;
12852
12853 for (var _i2 = 0, l = self.length; _i2 < l; _i2++) {
12854 var ele = self[_i2];
12855
12856 if (addToPool && !ele.removed()) {
12857 // don't need to handle this ele
12858 continue;
12859 } // keep nodes first in the array and edges after
12860
12861
12862 if (ele.isNode()) {
12863 // put to front of array if node
12864 nodes.push(ele);
12865 } else {
12866 // put to end of array if edge
12867 edges.push(ele);
12868 }
12869 }
12870
12871 elements = nodes.concat(edges);
12872 var i;
12873
12874 var removeFromElements = function removeFromElements() {
12875 elements.splice(i, 1);
12876 i--;
12877 }; // now, restore each element
12878
12879
12880 for (i = 0; i < elements.length; i++) {
12881 var _ele = elements[i];
12882 var _private = _ele._private;
12883 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
12884
12885 _ele.clearTraversalCache(); // set id and validate
12886
12887
12888 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
12889 _data3.id = idFactory.generate(cy, _ele);
12890 } else if (number(_data3.id)) {
12891 _data3.id = '' + _data3.id; // now it's a string
12892 } else if (emptyString(_data3.id) || !string(_data3.id)) {
12893 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
12894
12895 removeFromElements();
12896 continue;
12897 } else if (cy.hasElementWithId(_data3.id)) {
12898 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
12899
12900 removeFromElements();
12901 continue;
12902 }
12903
12904 var id = _data3.id; // id is finalised, now let's keep a ref
12905
12906 if (_ele.isNode()) {
12907 // extra checks for nodes
12908 var pos = _private.position; // make sure the nodes have a defined position
12909
12910 if (pos.x == null) {
12911 pos.x = 0;
12912 }
12913
12914 if (pos.y == null) {
12915 pos.y = 0;
12916 }
12917 }
12918
12919 if (_ele.isEdge()) {
12920 // extra checks for edges
12921 var edge = _ele;
12922 var fields = ['source', 'target'];
12923 var fieldsLength = fields.length;
12924 var badSourceOrTarget = false;
12925
12926 for (var j = 0; j < fieldsLength; j++) {
12927 var field = fields[j];
12928 var val = _data3[field];
12929
12930 if (number(val)) {
12931 val = _data3[field] = '' + _data3[field]; // now string
12932 }
12933
12934 if (val == null || val === '') {
12935 // can't create if source or target is not defined properly
12936 error('Can not create edge `' + id + '` with unspecified ' + field);
12937 badSourceOrTarget = true;
12938 } else if (!cy.hasElementWithId(val)) {
12939 // can't create edge if one of its nodes doesn't exist
12940 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
12941 badSourceOrTarget = true;
12942 }
12943 }
12944
12945 if (badSourceOrTarget) {
12946 removeFromElements();
12947 continue;
12948 } // can't create this
12949
12950
12951 var src = cy.getElementById(_data3.source);
12952 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
12953
12954 if (src.same(tgt)) {
12955 src._private.edges.push(edge);
12956 } else {
12957 src._private.edges.push(edge);
12958
12959 tgt._private.edges.push(edge);
12960 }
12961
12962 edge._private.source = src;
12963 edge._private.target = tgt;
12964 } // if is edge
12965 // create mock ids / indexes maps for element so it can be used like collections
12966
12967
12968 _private.map = new Map$1();
12969
12970 _private.map.set(id, {
12971 ele: _ele,
12972 index: 0
12973 });
12974
12975 _private.removed = false;
12976
12977 if (addToPool) {
12978 cy.addToPool(_ele);
12979 }
12980 } // for each element
12981 // do compound node sanity checks
12982
12983
12984 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
12985 // each node
12986 var node = nodes[_i3];
12987 var _data4 = node._private.data;
12988
12989 if (number(_data4.parent)) {
12990 // then automake string
12991 _data4.parent = '' + _data4.parent;
12992 }
12993
12994 var parentId = _data4.parent;
12995 var specifiedParent = parentId != null;
12996
12997 if (specifiedParent) {
12998 var parent = cy.getElementById(parentId);
12999
13000 if (parent.empty()) {
13001 // non-existant parent; just remove it
13002 _data4.parent = undefined;
13003 } else {
13004 var selfAsParent = false;
13005 var ancestor = parent;
13006
13007 while (!ancestor.empty()) {
13008 if (node.same(ancestor)) {
13009 // mark self as parent and remove from data
13010 selfAsParent = true;
13011 _data4.parent = undefined; // remove parent reference
13012 // exit or we loop forever
13013
13014 break;
13015 }
13016
13017 ancestor = ancestor.parent();
13018 }
13019
13020 if (!selfAsParent) {
13021 // connect with children
13022 parent[0]._private.children.push(node);
13023
13024 node._private.parent = parent[0]; // let the core know we have a compound graph
13025
13026 cy_p.hasCompoundNodes = true;
13027 }
13028 } // else
13029
13030 } // if specified parent
13031
13032 } // for each node
13033
13034
13035 if (elements.length > 0) {
13036 var restored = new Collection(cy, elements);
13037
13038 for (var _i4 = 0; _i4 < restored.length; _i4++) {
13039 var _ele2 = restored[_i4];
13040
13041 if (_ele2.isNode()) {
13042 continue;
13043 } // adding an edge invalidates the traversal caches for the parallel edges
13044
13045
13046 _ele2.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13047
13048
13049 _ele2.source().clearTraversalCache();
13050
13051 _ele2.target().clearTraversalCache();
13052 }
13053
13054 var toUpdateStyle;
13055
13056 if (cy_p.hasCompoundNodes) {
13057 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13058 } else {
13059 toUpdateStyle = restored;
13060 }
13061
13062 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13063
13064 if (notifyRenderer) {
13065 restored.emitAndNotify('add');
13066 } else if (addToPool) {
13067 restored.emit('add');
13068 }
13069 }
13070
13071 return self; // chainability
13072};
13073
13074elesfn$u.removed = function () {
13075 var ele = this[0];
13076 return ele && ele._private.removed;
13077};
13078
13079elesfn$u.inside = function () {
13080 var ele = this[0];
13081 return ele && !ele._private.removed;
13082};
13083
13084elesfn$u.remove = function () {
13085 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13086 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13087 var self = this;
13088 var elesToRemove = [];
13089 var elesToRemoveIds = {};
13090 var cy = self._private.cy; // add connected edges
13091
13092 function addConnectedEdges(node) {
13093 var edges = node._private.edges;
13094
13095 for (var i = 0; i < edges.length; i++) {
13096 add(edges[i]);
13097 }
13098 } // add descendant nodes
13099
13100
13101 function addChildren(node) {
13102 var children = node._private.children;
13103
13104 for (var i = 0; i < children.length; i++) {
13105 add(children[i]);
13106 }
13107 }
13108
13109 function add(ele) {
13110 var alreadyAdded = elesToRemoveIds[ele.id()];
13111
13112 if (removeFromPool && ele.removed() || alreadyAdded) {
13113 return;
13114 } else {
13115 elesToRemoveIds[ele.id()] = true;
13116 }
13117
13118 if (ele.isNode()) {
13119 elesToRemove.push(ele); // nodes are removed last
13120
13121 addConnectedEdges(ele);
13122 addChildren(ele);
13123 } else {
13124 elesToRemove.unshift(ele); // edges are removed first
13125 }
13126 } // make the list of elements to remove
13127 // (may be removing more than specified due to connected edges etc)
13128
13129
13130 for (var i = 0, l = self.length; i < l; i++) {
13131 var ele = self[i];
13132 add(ele);
13133 }
13134
13135 function removeEdgeRef(node, edge) {
13136 var connectedEdges = node._private.edges;
13137 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13138
13139 node.clearTraversalCache();
13140 }
13141
13142 function removeParallelRef(pllEdge) {
13143 // removing an edge invalidates the traversal caches for the parallel edges
13144 pllEdge.clearTraversalCache();
13145 }
13146
13147 var alteredParents = [];
13148 alteredParents.ids = {};
13149
13150 function removeChildRef(parent, ele) {
13151 ele = ele[0];
13152 parent = parent[0];
13153 var children = parent._private.children;
13154 var pid = parent.id();
13155 removeFromArray(children, ele); // remove parent => child ref
13156
13157 ele._private.parent = null; // remove child => parent ref
13158
13159 if (!alteredParents.ids[pid]) {
13160 alteredParents.ids[pid] = true;
13161 alteredParents.push(parent);
13162 }
13163 }
13164
13165 self.dirtyCompoundBoundsCache();
13166
13167 if (removeFromPool) {
13168 cy.removeFromPool(elesToRemove); // remove from core pool
13169 }
13170
13171 for (var _i5 = 0; _i5 < elesToRemove.length; _i5++) {
13172 var _ele3 = elesToRemove[_i5];
13173
13174 if (_ele3.isEdge()) {
13175 // remove references to this edge in its connected nodes
13176 var src = _ele3.source()[0];
13177
13178 var tgt = _ele3.target()[0];
13179
13180 removeEdgeRef(src, _ele3);
13181 removeEdgeRef(tgt, _ele3);
13182
13183 var pllEdges = _ele3.parallelEdges();
13184
13185 for (var j = 0; j < pllEdges.length; j++) {
13186 var pllEdge = pllEdges[j];
13187 removeParallelRef(pllEdge);
13188
13189 if (pllEdge.isBundledBezier()) {
13190 pllEdge.dirtyBoundingBoxCache();
13191 }
13192 }
13193 } else {
13194 // remove reference to parent
13195 var parent = _ele3.parent();
13196
13197 if (parent.length !== 0) {
13198 removeChildRef(parent, _ele3);
13199 }
13200 }
13201
13202 if (removeFromPool) {
13203 // mark as removed
13204 _ele3._private.removed = true;
13205 }
13206 } // check to see if we have a compound graph or not
13207
13208
13209 var elesStillInside = cy._private.elements;
13210 cy._private.hasCompoundNodes = false;
13211
13212 for (var _i6 = 0; _i6 < elesStillInside.length; _i6++) {
13213 var _ele4 = elesStillInside[_i6];
13214
13215 if (_ele4.isParent()) {
13216 cy._private.hasCompoundNodes = true;
13217 break;
13218 }
13219 }
13220
13221 var removedElements = new Collection(this.cy(), elesToRemove);
13222
13223 if (removedElements.size() > 0) {
13224 // must manually notify since trigger won't do this automatically once removed
13225 if (notifyRenderer) {
13226 removedElements.emitAndNotify('remove');
13227 } else if (removeFromPool) {
13228 removedElements.emit('remove');
13229 }
13230 } // the parents who were modified by the removal need their style updated
13231
13232
13233 for (var _i7 = 0; _i7 < alteredParents.length; _i7++) {
13234 var _ele5 = alteredParents[_i7];
13235
13236 if (!removeFromPool || !_ele5.removed()) {
13237 _ele5.updateStyle();
13238 }
13239 }
13240
13241 return removedElements;
13242};
13243
13244elesfn$u.move = function (struct) {
13245 var cy = this._private.cy;
13246 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
13247 // (our calls to remove/restore do not remove from the graph or make events)
13248
13249 var notifyRenderer = false;
13250 var modifyPool = false;
13251
13252 var toString = function toString(id) {
13253 return id == null ? id : '' + id;
13254 }; // id must be string
13255
13256
13257 if (struct.source !== undefined || struct.target !== undefined) {
13258 var srcId = toString(struct.source);
13259 var tgtId = toString(struct.target);
13260 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13261 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13262
13263 if (srcExists || tgtExists) {
13264 cy.batch(function () {
13265 // avoid duplicate style updates
13266 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13267
13268 eles.emitAndNotify('moveout');
13269
13270 for (var i = 0; i < eles.length; i++) {
13271 var ele = eles[i];
13272 var _data5 = ele._private.data;
13273
13274 if (ele.isEdge()) {
13275 if (srcExists) {
13276 _data5.source = srcId;
13277 }
13278
13279 if (tgtExists) {
13280 _data5.target = tgtId;
13281 }
13282 }
13283 }
13284
13285 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13286 });
13287 eles.emitAndNotify('move');
13288 }
13289 } else if (struct.parent !== undefined) {
13290 // move node to new parent
13291 var parentId = toString(struct.parent);
13292 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13293
13294 if (parentExists) {
13295 var pidToAssign = parentId === null ? undefined : parentId;
13296 cy.batch(function () {
13297 // avoid duplicate style updates
13298 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13299
13300 updated.emitAndNotify('moveout');
13301
13302 for (var i = 0; i < eles.length; i++) {
13303 var ele = eles[i];
13304 var _data6 = ele._private.data;
13305
13306 if (ele.isNode()) {
13307 _data6.parent = pidToAssign;
13308 }
13309 }
13310
13311 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13312 });
13313 eles.emitAndNotify('move');
13314 }
13315 }
13316
13317 return this;
13318};
13319
13320[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) {
13321 extend(elesfn$u, props);
13322});
13323
13324var corefn = {
13325 add: function add(opts) {
13326 var elements;
13327 var cy = this; // add the elements
13328
13329 if (elementOrCollection(opts)) {
13330 var eles = opts;
13331
13332 if (eles._private.cy === cy) {
13333 // same instance => just restore
13334 elements = eles.restore();
13335 } else {
13336 // otherwise, copy from json
13337 var jsons = [];
13338
13339 for (var i = 0; i < eles.length; i++) {
13340 var ele = eles[i];
13341 jsons.push(ele.json());
13342 }
13343
13344 elements = new Collection(cy, jsons);
13345 }
13346 } // specify an array of options
13347 else if (array(opts)) {
13348 var _jsons = opts;
13349 elements = new Collection(cy, _jsons);
13350 } // specify via opts.nodes and opts.edges
13351 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13352 var elesByGroup = opts;
13353 var _jsons2 = [];
13354 var grs = ['nodes', 'edges'];
13355
13356 for (var _i = 0, il = grs.length; _i < il; _i++) {
13357 var group = grs[_i];
13358 var elesArray = elesByGroup[group];
13359
13360 if (array(elesArray)) {
13361 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13362 var json = extend({
13363 group: group
13364 }, elesArray[j]);
13365
13366 _jsons2.push(json);
13367 }
13368 }
13369 }
13370
13371 elements = new Collection(cy, _jsons2);
13372 } // specify options for one element
13373 else {
13374 var _json = opts;
13375 elements = new Element(cy, _json).collection();
13376 }
13377
13378 return elements;
13379 },
13380 remove: function remove(collection) {
13381 if (elementOrCollection(collection)) ; else if (string(collection)) {
13382 var selector = collection;
13383 collection = this.$(selector);
13384 }
13385
13386 return collection.remove();
13387 }
13388};
13389
13390/* global Float32Array */
13391
13392/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13393function generateCubicBezier(mX1, mY1, mX2, mY2) {
13394 var NEWTON_ITERATIONS = 4,
13395 NEWTON_MIN_SLOPE = 0.001,
13396 SUBDIVISION_PRECISION = 0.0000001,
13397 SUBDIVISION_MAX_ITERATIONS = 10,
13398 kSplineTableSize = 11,
13399 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
13400 float32ArraySupported = typeof Float32Array !== 'undefined';
13401 /* Must contain four arguments. */
13402
13403 if (arguments.length !== 4) {
13404 return false;
13405 }
13406 /* Arguments must be numbers. */
13407
13408
13409 for (var i = 0; i < 4; ++i) {
13410 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
13411 return false;
13412 }
13413 }
13414 /* X values must be in the [0, 1] range. */
13415
13416
13417 mX1 = Math.min(mX1, 1);
13418 mX2 = Math.min(mX2, 1);
13419 mX1 = Math.max(mX1, 0);
13420 mX2 = Math.max(mX2, 0);
13421 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
13422
13423 function A(aA1, aA2) {
13424 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
13425 }
13426
13427 function B(aA1, aA2) {
13428 return 3.0 * aA2 - 6.0 * aA1;
13429 }
13430
13431 function C(aA1) {
13432 return 3.0 * aA1;
13433 }
13434
13435 function calcBezier(aT, aA1, aA2) {
13436 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
13437 }
13438
13439 function getSlope(aT, aA1, aA2) {
13440 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
13441 }
13442
13443 function newtonRaphsonIterate(aX, aGuessT) {
13444 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
13445 var currentSlope = getSlope(aGuessT, mX1, mX2);
13446
13447 if (currentSlope === 0.0) {
13448 return aGuessT;
13449 }
13450
13451 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
13452 aGuessT -= currentX / currentSlope;
13453 }
13454
13455 return aGuessT;
13456 }
13457
13458 function calcSampleValues() {
13459 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
13460 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
13461 }
13462 }
13463
13464 function binarySubdivide(aX, aA, aB) {
13465 var currentX,
13466 currentT,
13467 i = 0;
13468
13469 do {
13470 currentT = aA + (aB - aA) / 2.0;
13471 currentX = calcBezier(currentT, mX1, mX2) - aX;
13472
13473 if (currentX > 0.0) {
13474 aB = currentT;
13475 } else {
13476 aA = currentT;
13477 }
13478 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
13479
13480 return currentT;
13481 }
13482
13483 function getTForX(aX) {
13484 var intervalStart = 0.0,
13485 currentSample = 1,
13486 lastSample = kSplineTableSize - 1;
13487
13488 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
13489 intervalStart += kSampleStepSize;
13490 }
13491
13492 --currentSample;
13493 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
13494 guessForT = intervalStart + dist * kSampleStepSize,
13495 initialSlope = getSlope(guessForT, mX1, mX2);
13496
13497 if (initialSlope >= NEWTON_MIN_SLOPE) {
13498 return newtonRaphsonIterate(aX, guessForT);
13499 } else if (initialSlope === 0.0) {
13500 return guessForT;
13501 } else {
13502 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
13503 }
13504 }
13505
13506 var _precomputed = false;
13507
13508 function precompute() {
13509 _precomputed = true;
13510
13511 if (mX1 !== mY1 || mX2 !== mY2) {
13512 calcSampleValues();
13513 }
13514 }
13515
13516 var f = function f(aX) {
13517 if (!_precomputed) {
13518 precompute();
13519 }
13520
13521 if (mX1 === mY1 && mX2 === mY2) {
13522 return aX;
13523 }
13524
13525 if (aX === 0) {
13526 return 0;
13527 }
13528
13529 if (aX === 1) {
13530 return 1;
13531 }
13532
13533 return calcBezier(getTForX(aX), mY1, mY2);
13534 };
13535
13536 f.getControlPoints = function () {
13537 return [{
13538 x: mX1,
13539 y: mY1
13540 }, {
13541 x: mX2,
13542 y: mY2
13543 }];
13544 };
13545
13546 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
13547
13548 f.toString = function () {
13549 return str;
13550 };
13551
13552 return f;
13553}
13554
13555/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13556
13557/* 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
13558 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
13559var generateSpringRK4 = function () {
13560 function springAccelerationForState(state) {
13561 return -state.tension * state.x - state.friction * state.v;
13562 }
13563
13564 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
13565 var state = {
13566 x: initialState.x + derivative.dx * dt,
13567 v: initialState.v + derivative.dv * dt,
13568 tension: initialState.tension,
13569 friction: initialState.friction
13570 };
13571 return {
13572 dx: state.v,
13573 dv: springAccelerationForState(state)
13574 };
13575 }
13576
13577 function springIntegrateState(state, dt) {
13578 var a = {
13579 dx: state.v,
13580 dv: springAccelerationForState(state)
13581 },
13582 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
13583 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
13584 d = springEvaluateStateWithDerivative(state, dt, c),
13585 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
13586 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
13587 state.x = state.x + dxdt * dt;
13588 state.v = state.v + dvdt * dt;
13589 return state;
13590 }
13591
13592 return function springRK4Factory(tension, friction, duration) {
13593 var initState = {
13594 x: -1,
13595 v: 0,
13596 tension: null,
13597 friction: null
13598 },
13599 path = [0],
13600 time_lapsed = 0,
13601 tolerance = 1 / 10000,
13602 DT = 16 / 1000,
13603 have_duration,
13604 dt,
13605 last_state;
13606 tension = parseFloat(tension) || 500;
13607 friction = parseFloat(friction) || 20;
13608 duration = duration || null;
13609 initState.tension = tension;
13610 initState.friction = friction;
13611 have_duration = duration !== null;
13612 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
13613
13614 if (have_duration) {
13615 /* Run the simulation without a duration. */
13616 time_lapsed = springRK4Factory(tension, friction);
13617 /* Compute the adjusted time delta. */
13618
13619 dt = time_lapsed / duration * DT;
13620 } else {
13621 dt = DT;
13622 }
13623
13624 for (;;) {
13625 /* Next/step function .*/
13626 last_state = springIntegrateState(last_state || initState, dt);
13627 /* Store the position. */
13628
13629 path.push(1 + last_state.x);
13630 time_lapsed += 16;
13631 /* If the change threshold is reached, break. */
13632
13633 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
13634 break;
13635 }
13636 }
13637 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
13638 computed path and returns a snapshot of the position according to a given percentComplete. */
13639
13640
13641 return !have_duration ? time_lapsed : function (percentComplete) {
13642 return path[percentComplete * (path.length - 1) | 0];
13643 };
13644 };
13645}();
13646
13647var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
13648 var bezier = generateCubicBezier(t1, p1, t2, p2);
13649 return function (start, end, percent) {
13650 return start + (end - start) * bezier(percent);
13651 };
13652};
13653
13654var easings = {
13655 'linear': function linear(start, end, percent) {
13656 return start + (end - start) * percent;
13657 },
13658 // default easings
13659 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
13660 'ease-in': cubicBezier(0.42, 0, 1, 1),
13661 'ease-out': cubicBezier(0, 0, 0.58, 1),
13662 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
13663 // sine
13664 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
13665 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
13666 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
13667 // quad
13668 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
13669 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
13670 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
13671 // cubic
13672 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
13673 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
13674 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
13675 // quart
13676 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
13677 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
13678 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
13679 // quint
13680 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
13681 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
13682 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
13683 // expo
13684 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
13685 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
13686 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
13687 // circ
13688 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
13689 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
13690 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
13691 // user param easings...
13692 'spring': function spring(tension, friction, duration) {
13693 if (duration === 0) {
13694 // can't get a spring w/ duration 0
13695 return easings.linear; // duration 0 => jump to end so impl doesn't matter
13696 }
13697
13698 var spring = generateSpringRK4(tension, friction, duration);
13699 return function (start, end, percent) {
13700 return start + (end - start) * spring(percent);
13701 };
13702 },
13703 'cubic-bezier': cubicBezier
13704};
13705
13706function getEasedValue(type, start, end, percent, easingFn) {
13707 if (percent === 1) {
13708 return end;
13709 }
13710
13711 if (start === end) {
13712 return end;
13713 }
13714
13715 var val = easingFn(start, end, percent);
13716
13717 if (type == null) {
13718 return val;
13719 }
13720
13721 if (type.roundValue || type.color) {
13722 val = Math.round(val);
13723 }
13724
13725 if (type.min !== undefined) {
13726 val = Math.max(val, type.min);
13727 }
13728
13729 if (type.max !== undefined) {
13730 val = Math.min(val, type.max);
13731 }
13732
13733 return val;
13734}
13735
13736function getValue(prop, spec) {
13737 if (prop.pfValue != null || prop.value != null) {
13738 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
13739 return prop.pfValue;
13740 } else {
13741 return prop.value;
13742 }
13743 } else {
13744 return prop;
13745 }
13746}
13747
13748function ease(startProp, endProp, percent, easingFn, propSpec) {
13749 var type = propSpec != null ? propSpec.type : null;
13750
13751 if (percent < 0) {
13752 percent = 0;
13753 } else if (percent > 1) {
13754 percent = 1;
13755 }
13756
13757 var start = getValue(startProp, propSpec);
13758 var end = getValue(endProp, propSpec);
13759
13760 if (number(start) && number(end)) {
13761 return getEasedValue(type, start, end, percent, easingFn);
13762 } else if (array(start) && array(end)) {
13763 var easedArr = [];
13764
13765 for (var i = 0; i < end.length; i++) {
13766 var si = start[i];
13767 var ei = end[i];
13768
13769 if (si != null && ei != null) {
13770 var val = getEasedValue(type, si, ei, percent, easingFn);
13771 easedArr.push(val);
13772 } else {
13773 easedArr.push(ei);
13774 }
13775 }
13776
13777 return easedArr;
13778 }
13779
13780 return undefined;
13781}
13782
13783function step(self, ani, now, isCore) {
13784 var isEles = !isCore;
13785 var _p = self._private;
13786 var ani_p = ani._private;
13787 var pEasing = ani_p.easing;
13788 var startTime = ani_p.startTime;
13789 var cy = isCore ? self : self.cy();
13790 var style = cy.style();
13791
13792 if (!ani_p.easingImpl) {
13793 if (pEasing == null) {
13794 // use default
13795 ani_p.easingImpl = easings['linear'];
13796 } else {
13797 // then define w/ name
13798 var easingVals;
13799
13800 if (string(pEasing)) {
13801 var easingProp = style.parse('transition-timing-function', pEasing);
13802 easingVals = easingProp.value;
13803 } else {
13804 // then assume preparsed array
13805 easingVals = pEasing;
13806 }
13807
13808 var name, args;
13809
13810 if (string(easingVals)) {
13811 name = easingVals;
13812 args = [];
13813 } else {
13814 name = easingVals[1];
13815 args = easingVals.slice(2).map(function (n) {
13816 return +n;
13817 });
13818 }
13819
13820 if (args.length > 0) {
13821 // create with args
13822 if (name === 'spring') {
13823 args.push(ani_p.duration); // need duration to generate spring
13824 }
13825
13826 ani_p.easingImpl = easings[name].apply(null, args);
13827 } else {
13828 // static impl by name
13829 ani_p.easingImpl = easings[name];
13830 }
13831 }
13832 }
13833
13834 var easing = ani_p.easingImpl;
13835 var percent;
13836
13837 if (ani_p.duration === 0) {
13838 percent = 1;
13839 } else {
13840 percent = (now - startTime) / ani_p.duration;
13841 }
13842
13843 if (ani_p.applying) {
13844 percent = ani_p.progress;
13845 }
13846
13847 if (percent < 0) {
13848 percent = 0;
13849 } else if (percent > 1) {
13850 percent = 1;
13851 }
13852
13853 if (ani_p.delay == null) {
13854 // then update
13855 var startPos = ani_p.startPosition;
13856 var endPos = ani_p.position;
13857
13858 if (endPos && isEles && !self.locked()) {
13859 var newPos = {};
13860
13861 if (valid(startPos.x, endPos.x)) {
13862 newPos.x = ease(startPos.x, endPos.x, percent, easing);
13863 }
13864
13865 if (valid(startPos.y, endPos.y)) {
13866 newPos.y = ease(startPos.y, endPos.y, percent, easing);
13867 }
13868
13869 self.position(newPos);
13870 }
13871
13872 var startPan = ani_p.startPan;
13873 var endPan = ani_p.pan;
13874 var pan = _p.pan;
13875 var animatingPan = endPan != null && isCore;
13876
13877 if (animatingPan) {
13878 if (valid(startPan.x, endPan.x)) {
13879 pan.x = ease(startPan.x, endPan.x, percent, easing);
13880 }
13881
13882 if (valid(startPan.y, endPan.y)) {
13883 pan.y = ease(startPan.y, endPan.y, percent, easing);
13884 }
13885
13886 self.emit('pan');
13887 }
13888
13889 var startZoom = ani_p.startZoom;
13890 var endZoom = ani_p.zoom;
13891 var animatingZoom = endZoom != null && isCore;
13892
13893 if (animatingZoom) {
13894 if (valid(startZoom, endZoom)) {
13895 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
13896 }
13897
13898 self.emit('zoom');
13899 }
13900
13901 if (animatingPan || animatingZoom) {
13902 self.emit('viewport');
13903 }
13904
13905 var props = ani_p.style;
13906
13907 if (props && props.length > 0 && isEles) {
13908 for (var i = 0; i < props.length; i++) {
13909 var prop = props[i];
13910 var _name = prop.name;
13911 var end = prop;
13912 var start = ani_p.startStyle[_name];
13913 var propSpec = style.properties[start.name];
13914 var easedVal = ease(start, end, percent, easing, propSpec);
13915 style.overrideBypass(self, _name, easedVal);
13916 } // for props
13917
13918
13919 self.emit('style');
13920 } // if
13921
13922 }
13923
13924 ani_p.progress = percent;
13925 return percent;
13926}
13927
13928function valid(start, end) {
13929 if (start == null || end == null) {
13930 return false;
13931 }
13932
13933 if (number(start) && number(end)) {
13934 return true;
13935 } else if (start && end) {
13936 return true;
13937 }
13938
13939 return false;
13940}
13941
13942function startAnimation(self, ani, now, isCore) {
13943 var ani_p = ani._private;
13944 ani_p.started = true;
13945 ani_p.startTime = now - ani_p.progress * ani_p.duration;
13946}
13947
13948function stepAll(now, cy) {
13949 var eles = cy._private.aniEles;
13950 var doneEles = [];
13951
13952 function stepOne(ele, isCore) {
13953 var _p = ele._private;
13954 var current = _p.animation.current;
13955 var queue = _p.animation.queue;
13956 var ranAnis = false; // cancel all animations on display:none ele
13957
13958 if (!isCore && ele.pstyle('display').value === 'none') {
13959 // put all current and queue animations in this tick's current list
13960 // and empty the lists for the element
13961 current = current.splice(0, current.length).concat(queue.splice(0, queue.length)); // stop all animations
13962
13963 for (var i = 0; i < current.length; i++) {
13964 current[i].stop();
13965 }
13966 } // if nothing currently animating, get something from the queue
13967
13968
13969 if (current.length === 0) {
13970 var next = queue.shift();
13971
13972 if (next) {
13973 current.push(next);
13974 }
13975 }
13976
13977 var callbacks = function callbacks(_callbacks) {
13978 for (var j = _callbacks.length - 1; j >= 0; j--) {
13979 var cb = _callbacks[j];
13980 cb();
13981 }
13982
13983 _callbacks.splice(0, _callbacks.length);
13984 }; // step and remove if done
13985
13986
13987 for (var _i = current.length - 1; _i >= 0; _i--) {
13988 var ani = current[_i];
13989 var ani_p = ani._private;
13990
13991 if (ani_p.stopped) {
13992 current.splice(_i, 1);
13993 ani_p.hooked = false;
13994 ani_p.playing = false;
13995 ani_p.started = false;
13996 callbacks(ani_p.frames);
13997 continue;
13998 }
13999
14000 if (!ani_p.playing && !ani_p.applying) {
14001 continue;
14002 } // an apply() while playing shouldn't do anything
14003
14004
14005 if (ani_p.playing && ani_p.applying) {
14006 ani_p.applying = false;
14007 }
14008
14009 if (!ani_p.started) {
14010 startAnimation(ele, ani, now);
14011 }
14012
14013 step(ele, ani, now, isCore);
14014
14015 if (ani_p.applying) {
14016 ani_p.applying = false;
14017 }
14018
14019 callbacks(ani_p.frames);
14020
14021 if (ani_p.step != null) {
14022 ani_p.step(now);
14023 }
14024
14025 if (ani.completed()) {
14026 current.splice(_i, 1);
14027 ani_p.hooked = false;
14028 ani_p.playing = false;
14029 ani_p.started = false;
14030 callbacks(ani_p.completes);
14031 }
14032
14033 ranAnis = true;
14034 }
14035
14036 if (!isCore && current.length === 0 && queue.length === 0) {
14037 doneEles.push(ele);
14038 }
14039
14040 return ranAnis;
14041 } // stepElement
14042 // handle all eles
14043
14044
14045 var ranEleAni = false;
14046
14047 for (var e = 0; e < eles.length; e++) {
14048 var ele = eles[e];
14049 var handledThisEle = stepOne(ele);
14050 ranEleAni = ranEleAni || handledThisEle;
14051 } // each element
14052
14053
14054 var ranCoreAni = stepOne(cy, true); // notify renderer
14055
14056 if (ranEleAni || ranCoreAni) {
14057 if (eles.length > 0) {
14058 cy.notify('draw', eles);
14059 } else {
14060 cy.notify('draw');
14061 }
14062 } // remove elements from list of currently animating if its queues are empty
14063
14064
14065 eles.unmerge(doneEles);
14066 cy.emit('step');
14067} // stepAll
14068
14069var corefn$1 = {
14070 // pull in animation functions
14071 animate: define$3.animate(),
14072 animation: define$3.animation(),
14073 animated: define$3.animated(),
14074 clearQueue: define$3.clearQueue(),
14075 delay: define$3.delay(),
14076 delayAnimation: define$3.delayAnimation(),
14077 stop: define$3.stop(),
14078 addToAnimationPool: function addToAnimationPool(eles) {
14079 var cy = this;
14080
14081 if (!cy.styleEnabled()) {
14082 return;
14083 } // save cycles when no style used
14084
14085
14086 cy._private.aniEles.merge(eles);
14087 },
14088 stopAnimationLoop: function stopAnimationLoop() {
14089 this._private.animationsRunning = false;
14090 },
14091 startAnimationLoop: function startAnimationLoop() {
14092 var cy = this;
14093 cy._private.animationsRunning = true;
14094
14095 if (!cy.styleEnabled()) {
14096 return;
14097 } // save cycles when no style used
14098 // NB the animation loop will exec in headless environments if style enabled
14099 // and explicit cy.destroy() is necessary to stop the loop
14100
14101
14102 function headlessStep() {
14103 if (!cy._private.animationsRunning) {
14104 return;
14105 }
14106
14107 requestAnimationFrame(function animationStep(now) {
14108 stepAll(now, cy);
14109 headlessStep();
14110 });
14111 }
14112
14113 var renderer = cy.renderer();
14114
14115 if (renderer && renderer.beforeRender) {
14116 // let the renderer schedule animations
14117 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14118 stepAll(now, cy);
14119 }, renderer.beforeRenderPriorities.animations);
14120 } else {
14121 // manage the animation loop ourselves
14122 headlessStep(); // first call
14123 }
14124 }
14125};
14126
14127var emitterOptions$1 = {
14128 qualifierCompare: function qualifierCompare(selector1, selector2) {
14129 if (selector1 == null || selector2 == null) {
14130 return selector1 == null && selector2 == null;
14131 } else {
14132 return selector1.sameText(selector2);
14133 }
14134 },
14135 eventMatches: function eventMatches(cy, listener, eventObj) {
14136 var selector = listener.qualifier;
14137
14138 if (selector != null) {
14139 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14140 }
14141
14142 return true;
14143 },
14144 addEventFields: function addEventFields(cy, evt) {
14145 evt.cy = cy;
14146 evt.target = cy;
14147 },
14148 callbackContext: function callbackContext(cy, listener, eventObj) {
14149 return listener.qualifier != null ? eventObj.target : cy;
14150 }
14151};
14152
14153var argSelector$1 = function argSelector(arg) {
14154 if (string(arg)) {
14155 return new Selector(arg);
14156 } else {
14157 return arg;
14158 }
14159};
14160
14161var elesfn$v = {
14162 createEmitter: function createEmitter() {
14163 var _p = this._private;
14164
14165 if (!_p.emitter) {
14166 _p.emitter = new Emitter(emitterOptions$1, this);
14167 }
14168
14169 return this;
14170 },
14171 emitter: function emitter() {
14172 return this._private.emitter;
14173 },
14174 on: function on(events, selector, callback) {
14175 this.emitter().on(events, argSelector$1(selector), callback);
14176 return this;
14177 },
14178 removeListener: function removeListener(events, selector, callback) {
14179 this.emitter().removeListener(events, argSelector$1(selector), callback);
14180 return this;
14181 },
14182 removeAllListeners: function removeAllListeners() {
14183 this.emitter().removeAllListeners();
14184 return this;
14185 },
14186 one: function one(events, selector, callback) {
14187 this.emitter().one(events, argSelector$1(selector), callback);
14188 return this;
14189 },
14190 once: function once(events, selector, callback) {
14191 this.emitter().one(events, argSelector$1(selector), callback);
14192 return this;
14193 },
14194 emit: function emit(events, extraParams) {
14195 this.emitter().emit(events, extraParams);
14196 return this;
14197 },
14198 emitAndNotify: function emitAndNotify(event, eles) {
14199 this.emit(event);
14200 this.notify(event, eles);
14201 return this;
14202 }
14203};
14204define$3.eventAliasesOn(elesfn$v);
14205
14206var corefn$2 = {
14207 png: function png(options) {
14208 var renderer = this._private.renderer;
14209 options = options || {};
14210 return renderer.png(options);
14211 },
14212 jpg: function jpg(options) {
14213 var renderer = this._private.renderer;
14214 options = options || {};
14215 options.bg = options.bg || '#fff';
14216 return renderer.jpg(options);
14217 }
14218};
14219corefn$2.jpeg = corefn$2.jpg;
14220
14221var corefn$3 = {
14222 layout: function layout(options) {
14223 var cy = this;
14224
14225 if (options == null) {
14226 error('Layout options must be specified to make a layout');
14227 return;
14228 }
14229
14230 if (options.name == null) {
14231 error('A `name` must be specified to make a layout');
14232 return;
14233 }
14234
14235 var name = options.name;
14236 var Layout = cy.extension('layout', name);
14237
14238 if (Layout == null) {
14239 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14240 return;
14241 }
14242
14243 var eles;
14244
14245 if (string(options.eles)) {
14246 eles = cy.$(options.eles);
14247 } else {
14248 eles = options.eles != null ? options.eles : cy.$();
14249 }
14250
14251 var layout = new Layout(extend({}, options, {
14252 cy: cy,
14253 eles: eles
14254 }));
14255 return layout;
14256 }
14257};
14258corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
14259
14260var corefn$4 = {
14261 notify: function notify(eventName, eventEles) {
14262 var _p = this._private;
14263
14264 if (this.batching()) {
14265 _p.batchNotifications = _p.batchNotifications || {};
14266 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14267
14268 if (eventEles != null) {
14269 eles.merge(eventEles);
14270 }
14271
14272 return; // notifications are disabled during batching
14273 }
14274
14275 if (!_p.notificationsEnabled) {
14276 return;
14277 } // exit on disabled
14278
14279
14280 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
14281
14282 if (this.destroyed() || !renderer) {
14283 return;
14284 }
14285
14286 renderer.notify(eventName, eventEles);
14287 },
14288 notifications: function notifications(bool) {
14289 var p = this._private;
14290
14291 if (bool === undefined) {
14292 return p.notificationsEnabled;
14293 } else {
14294 p.notificationsEnabled = bool ? true : false;
14295 }
14296
14297 return this;
14298 },
14299 noNotifications: function noNotifications(callback) {
14300 this.notifications(false);
14301 callback();
14302 this.notifications(true);
14303 },
14304 batching: function batching() {
14305 return this._private.batchCount > 0;
14306 },
14307 startBatch: function startBatch() {
14308 var _p = this._private;
14309
14310 if (_p.batchCount == null) {
14311 _p.batchCount = 0;
14312 }
14313
14314 if (_p.batchCount === 0) {
14315 _p.batchStyleEles = this.collection();
14316 _p.batchNotifications = {};
14317 }
14318
14319 _p.batchCount++;
14320 return this;
14321 },
14322 endBatch: function endBatch() {
14323 var _p = this._private;
14324
14325 if (_p.batchCount === 0) {
14326 return this;
14327 }
14328
14329 _p.batchCount--;
14330
14331 if (_p.batchCount === 0) {
14332 // update style for dirty eles
14333 _p.batchStyleEles.updateStyle();
14334
14335 var renderer = this.renderer(); // notify the renderer of queued eles and event types
14336
14337 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14338 var eles = _p.batchNotifications[eventName];
14339
14340 if (eles.empty()) {
14341 renderer.notify(eventName);
14342 } else {
14343 renderer.notify(eventName, eles);
14344 }
14345 });
14346 }
14347
14348 return this;
14349 },
14350 batch: function batch(callback) {
14351 this.startBatch();
14352 callback();
14353 this.endBatch();
14354 return this;
14355 },
14356 // for backwards compatibility
14357 batchData: function batchData(map) {
14358 var cy = this;
14359 return this.batch(function () {
14360 var ids = Object.keys(map);
14361
14362 for (var i = 0; i < ids.length; i++) {
14363 var id = ids[i];
14364 var data = map[id];
14365 var ele = cy.getElementById(id);
14366 ele.data(data);
14367 }
14368 });
14369 }
14370};
14371
14372var rendererDefaults = defaults({
14373 hideEdgesOnViewport: false,
14374 textureOnViewport: false,
14375 motionBlur: false,
14376 motionBlurOpacity: 0.05,
14377 pixelRatio: undefined,
14378 desktopTapThreshold: 4,
14379 touchTapThreshold: 8,
14380 wheelSensitivity: 1,
14381 debug: false,
14382 showFps: false
14383});
14384var corefn$5 = {
14385 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14386 var r = this._private.renderer;
14387 r.renderTo(context, zoom, pan, pxRatio);
14388 return this;
14389 },
14390 renderer: function renderer() {
14391 return this._private.renderer;
14392 },
14393 forceRender: function forceRender() {
14394 this.notify('draw');
14395 return this;
14396 },
14397 resize: function resize() {
14398 this.invalidateSize();
14399 this.emitAndNotify('resize');
14400 return this;
14401 },
14402 initRenderer: function initRenderer(options) {
14403 var cy = this;
14404 var RendererProto = cy.extension('renderer', options.name);
14405
14406 if (RendererProto == null) {
14407 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14408 return;
14409 }
14410
14411 if (options.wheelSensitivity !== undefined) {
14412 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.");
14413 }
14414
14415 var rOpts = rendererDefaults(options);
14416 rOpts.cy = cy;
14417 cy._private.renderer = new RendererProto(rOpts);
14418 this.notify('init');
14419 },
14420 destroyRenderer: function destroyRenderer() {
14421 var cy = this;
14422 cy.notify('destroy'); // destroy the renderer
14423
14424 var domEle = cy.container();
14425
14426 if (domEle) {
14427 domEle._cyreg = null;
14428
14429 while (domEle.childNodes.length > 0) {
14430 domEle.removeChild(domEle.childNodes[0]);
14431 }
14432 }
14433
14434 cy._private.renderer = null; // to be extra safe, remove the ref
14435
14436 cy.mutableElements().forEach(function (ele) {
14437 var _p = ele._private;
14438 _p.rscratch = {};
14439 _p.rstyle = {};
14440 _p.animation.current = [];
14441 _p.animation.queue = [];
14442 });
14443 },
14444 onRender: function onRender(fn) {
14445 return this.on('render', fn);
14446 },
14447 offRender: function offRender(fn) {
14448 return this.off('render', fn);
14449 }
14450};
14451corefn$5.invalidateDimensions = corefn$5.resize;
14452
14453var corefn$6 = {
14454 // get a collection
14455 // - empty collection on no args
14456 // - collection of elements in the graph on selector arg
14457 // - guarantee a returned collection when elements or collection specified
14458 collection: function collection(eles, opts) {
14459 if (string(eles)) {
14460 return this.$(eles);
14461 } else if (elementOrCollection(eles)) {
14462 return eles.collection();
14463 } else if (array(eles)) {
14464 return new Collection(this, eles, opts);
14465 }
14466
14467 return new Collection(this);
14468 },
14469 nodes: function nodes(selector) {
14470 var nodes = this.$(function (ele) {
14471 return ele.isNode();
14472 });
14473
14474 if (selector) {
14475 return nodes.filter(selector);
14476 }
14477
14478 return nodes;
14479 },
14480 edges: function edges(selector) {
14481 var edges = this.$(function (ele) {
14482 return ele.isEdge();
14483 });
14484
14485 if (selector) {
14486 return edges.filter(selector);
14487 }
14488
14489 return edges;
14490 },
14491 // search the graph like jQuery
14492 $: function $(selector) {
14493 var eles = this._private.elements;
14494
14495 if (selector) {
14496 return eles.filter(selector);
14497 } else {
14498 return eles.spawnSelf();
14499 }
14500 },
14501 mutableElements: function mutableElements() {
14502 return this._private.elements;
14503 }
14504}; // aliases
14505
14506corefn$6.elements = corefn$6.filter = corefn$6.$;
14507
14508var styfn = {}; // keys for style blocks, e.g. ttfftt
14509
14510var TRUE = 't';
14511var FALSE = 'f'; // (potentially expensive calculation)
14512// apply the style to the element based on
14513// - its bypass
14514// - what selectors match it
14515
14516styfn.apply = function (eles) {
14517 var self = this;
14518 var _p = self._private;
14519 var cy = _p.cy;
14520 var updatedEles = cy.collection();
14521
14522 if (_p.newStyle) {
14523 // clear style caches
14524 _p.contextStyles = {};
14525 _p.propDiffs = {};
14526 self.cleanElements(eles, true);
14527 }
14528
14529 for (var ie = 0; ie < eles.length; ie++) {
14530 var ele = eles[ie];
14531 var cxtMeta = self.getContextMeta(ele);
14532
14533 if (cxtMeta.empty) {
14534 continue;
14535 }
14536
14537 var cxtStyle = self.getContextStyle(cxtMeta);
14538 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
14539
14540 if (!_p.newStyle) {
14541 self.updateTransitions(ele, app.diffProps);
14542 }
14543
14544 var hintsDiff = self.updateStyleHints(ele);
14545
14546 if (hintsDiff) {
14547 updatedEles.merge(ele);
14548 }
14549 } // for elements
14550
14551
14552 _p.newStyle = false;
14553 return updatedEles;
14554};
14555
14556styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
14557 var self = this;
14558 var cache = self._private.propDiffs = self._private.propDiffs || {};
14559 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
14560 var cachedVal = cache[dualCxtKey];
14561
14562 if (cachedVal) {
14563 return cachedVal;
14564 }
14565
14566 var diffProps = [];
14567 var addedProp = {};
14568
14569 for (var i = 0; i < self.length; i++) {
14570 var cxt = self[i];
14571 var oldHasCxt = oldCxtKey[i] === TRUE;
14572 var newHasCxt = newCxtKey[i] === TRUE;
14573 var cxtHasDiffed = oldHasCxt !== newHasCxt;
14574 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
14575
14576 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
14577 var props = void 0;
14578
14579 if (cxtHasDiffed && cxtHasMappedProps) {
14580 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
14581 } else if (cxtHasDiffed) {
14582 props = cxt.properties; // need to check them all
14583 } else if (cxtHasMappedProps) {
14584 props = cxt.mappedProperties; // only need to check mapped
14585 }
14586
14587 for (var j = 0; j < props.length; j++) {
14588 var prop = props[j];
14589 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
14590 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
14591 // is cached)
14592
14593 var laterCxtOverrides = false;
14594
14595 for (var k = i + 1; k < self.length; k++) {
14596 var laterCxt = self[k];
14597 var hasLaterCxt = newCxtKey[k] === TRUE;
14598
14599 if (!hasLaterCxt) {
14600 continue;
14601 } // can't override unless the context is active
14602
14603
14604 laterCxtOverrides = laterCxt.properties[prop.name] != null;
14605
14606 if (laterCxtOverrides) {
14607 break;
14608 } // exit early as long as one later context overrides
14609
14610 }
14611
14612 if (!addedProp[name] && !laterCxtOverrides) {
14613 addedProp[name] = true;
14614 diffProps.push(name);
14615 }
14616 } // for props
14617
14618 } // if
14619
14620 } // for contexts
14621
14622
14623 cache[dualCxtKey] = diffProps;
14624 return diffProps;
14625};
14626
14627styfn.getContextMeta = function (ele) {
14628 var self = this;
14629 var cxtKey = '';
14630 var diffProps;
14631 var prevKey = ele._private.styleCxtKey || '';
14632
14633 if (self._private.newStyle) {
14634 prevKey = ''; // since we need to apply all style if a fresh stylesheet
14635 } // get the cxt key
14636
14637
14638 for (var i = 0; i < self.length; i++) {
14639 var context = self[i];
14640 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
14641
14642 if (contextSelectorMatches) {
14643 cxtKey += TRUE;
14644 } else {
14645 cxtKey += FALSE;
14646 }
14647 } // for context
14648
14649
14650 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
14651 ele._private.styleCxtKey = cxtKey;
14652 return {
14653 key: cxtKey,
14654 diffPropNames: diffProps,
14655 empty: diffProps.length === 0
14656 };
14657}; // gets a computed ele style object based on matched contexts
14658
14659
14660styfn.getContextStyle = function (cxtMeta) {
14661 var cxtKey = cxtMeta.key;
14662 var self = this;
14663 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
14664
14665 if (cxtStyles[cxtKey]) {
14666 return cxtStyles[cxtKey];
14667 }
14668
14669 var style = {
14670 _private: {
14671 key: cxtKey
14672 }
14673 };
14674
14675 for (var i = 0; i < self.length; i++) {
14676 var cxt = self[i];
14677 var hasCxt = cxtKey[i] === TRUE;
14678
14679 if (!hasCxt) {
14680 continue;
14681 }
14682
14683 for (var j = 0; j < cxt.properties.length; j++) {
14684 var prop = cxt.properties[j];
14685 style[prop.name] = prop;
14686 }
14687 }
14688
14689 cxtStyles[cxtKey] = style;
14690 return style;
14691};
14692
14693styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
14694 var self = this;
14695 var diffProps = cxtMeta.diffPropNames;
14696 var retDiffProps = {};
14697 var types = self.types;
14698
14699 for (var i = 0; i < diffProps.length; i++) {
14700 var diffPropName = diffProps[i];
14701 var cxtProp = cxtStyle[diffPropName];
14702 var eleProp = ele.pstyle(diffPropName);
14703
14704 if (!cxtProp) {
14705 // no context prop means delete
14706 if (!eleProp) {
14707 continue; // no existing prop means nothing needs to be removed
14708 // nb affects initial application on mapped values like control-point-distances
14709 } else if (eleProp.bypass) {
14710 cxtProp = {
14711 name: diffPropName,
14712 deleteBypassed: true
14713 };
14714 } else {
14715 cxtProp = {
14716 name: diffPropName,
14717 "delete": true
14718 };
14719 }
14720 } // save cycles when the context prop doesn't need to be applied
14721
14722
14723 if (eleProp === cxtProp) {
14724 continue;
14725 } // save cycles when a mapped context prop doesn't need to be applied
14726
14727
14728 if (cxtProp.mapped === types.fn // context prop is function mapper
14729 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
14730 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
14731 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
14732 ) {
14733 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
14734 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
14735
14736 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
14737
14738 if (fnValue === mapping.prevFnValue) {
14739 continue;
14740 }
14741 }
14742
14743 var retDiffProp = retDiffProps[diffPropName] = {
14744 prev: eleProp
14745 };
14746 self.applyParsedProperty(ele, cxtProp);
14747 retDiffProp.next = ele.pstyle(diffPropName);
14748
14749 if (retDiffProp.next && retDiffProp.next.bypass) {
14750 retDiffProp.next = retDiffProp.next.bypassed;
14751 }
14752 }
14753
14754 return {
14755 diffProps: retDiffProps
14756 };
14757};
14758
14759styfn.updateStyleHints = function (ele) {
14760 var _p = ele._private;
14761 var self = this;
14762 var propNames = self.propertyGroupNames;
14763 var propGrKeys = self.propertyGroupKeys;
14764
14765 var propHash = function propHash(ele, propNames, seedKey) {
14766 return self.getPropertiesHash(ele, propNames, seedKey);
14767 };
14768
14769 var oldStyleKey = _p.styleKey;
14770
14771 if (ele.removed()) {
14772 return false;
14773 }
14774
14775 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
14776 // but lazily -- only use non-default prop values to reduce the number of hashes
14777 //
14778
14779 var overriddenStyles = ele._private.style;
14780 propNames = Object.keys(overriddenStyles);
14781
14782 for (var i = 0; i < propGrKeys.length; i++) {
14783 var grKey = propGrKeys[i];
14784 _p.styleKeys[grKey] = 0;
14785 }
14786
14787 var updateGrKey = function updateGrKey(val, grKey) {
14788 return _p.styleKeys[grKey] = hashInt(val, _p.styleKeys[grKey]);
14789 };
14790
14791 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
14792 for (var j = 0; j < strVal.length; j++) {
14793 updateGrKey(strVal.charCodeAt(j), grKey);
14794 }
14795 }; // - hashing works on 32 bit ints b/c we use bitwise ops
14796 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
14797 // - raise up small numbers so more significant digits are seen by hashing
14798 // - make small numbers larger than a normal value to avoid collisions
14799 // - works in practice and it's relatively cheap
14800
14801
14802 var N = 2000000000;
14803
14804 var cleanNum = function cleanNum(val) {
14805 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
14806 };
14807
14808 for (var _i = 0; _i < propNames.length; _i++) {
14809 var name = propNames[_i];
14810 var parsedProp = overriddenStyles[name];
14811
14812 if (parsedProp == null) {
14813 continue;
14814 }
14815
14816 var propInfo = this.properties[name];
14817 var type = propInfo.type;
14818 var _grKey = propInfo.groupKey;
14819 var normalizedNumberVal = void 0;
14820
14821 if (propInfo.hashOverride != null) {
14822 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
14823 } else if (parsedProp.pfValue != null) {
14824 normalizedNumberVal = parsedProp.pfValue;
14825 } // might not be a number if it allows enums
14826
14827
14828 var numberVal = propInfo.enums == null ? parsedProp.value : null;
14829 var haveNormNum = normalizedNumberVal != null;
14830 var haveUnitedNum = numberVal != null;
14831 var haveNum = haveNormNum || haveUnitedNum;
14832 var units = parsedProp.units; // numbers are cheaper to hash than strings
14833 // 1 hash op vs n hash ops (for length n string)
14834
14835 if (type.number && haveNum) {
14836 var v = haveNormNum ? normalizedNumberVal : numberVal;
14837
14838 if (type.multiple) {
14839 for (var _i2 = 0; _i2 < v.length; _i2++) {
14840 updateGrKey(cleanNum(v[_i2]), _grKey);
14841 }
14842 } else {
14843 updateGrKey(cleanNum(v), _grKey);
14844 }
14845
14846 if (!haveNormNum && units != null) {
14847 updateGrKeyWStr(units, _grKey);
14848 }
14849 } else {
14850 updateGrKeyWStr(parsedProp.strValue, _grKey);
14851 }
14852 } // overall style key
14853 //
14854
14855
14856 var hash = 0;
14857
14858 for (var _i3 = 0; _i3 < propGrKeys.length; _i3++) {
14859 var _grKey2 = propGrKeys[_i3];
14860 var grHash = _p.styleKeys[_grKey2];
14861 hash = hashInt(grHash, hash);
14862 }
14863
14864 _p.styleKey = hash; // label dims
14865 //
14866
14867 var labelDimsKey = _p.labelDimsKey = _p.styleKeys.labelDimensions;
14868 _p.labelKey = propHash(ele, ['label'], labelDimsKey);
14869 _p.labelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.labelKey);
14870
14871 if (!isNode) {
14872 _p.sourceLabelKey = propHash(ele, ['source-label'], labelDimsKey);
14873 _p.sourceLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.sourceLabelKey);
14874 _p.targetLabelKey = propHash(ele, ['target-label'], labelDimsKey);
14875 _p.targetLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.targetLabelKey);
14876 } // node
14877 //
14878
14879
14880 if (isNode) {
14881 var _p$styleKeys = _p.styleKeys,
14882 nodeBody = _p$styleKeys.nodeBody,
14883 nodeBorder = _p$styleKeys.nodeBorder,
14884 backgroundImage = _p$styleKeys.backgroundImage,
14885 compound = _p$styleKeys.compound,
14886 pie = _p$styleKeys.pie;
14887 _p.nodeKey = hashIntsArray([nodeBorder, backgroundImage, compound, pie], nodeBody);
14888 _p.hasPie = pie != 0;
14889 }
14890
14891 return oldStyleKey !== _p.styleKey;
14892};
14893
14894styfn.clearStyleHints = function (ele) {
14895 var _p = ele._private;
14896 _p.styleKeys = {};
14897 _p.styleKey = null;
14898 _p.labelKey = null;
14899 _p.labelStyleKey = null;
14900 _p.sourceLabelKey = null;
14901 _p.sourceLabelStyleKey = null;
14902 _p.targetLabelKey = null;
14903 _p.targetLabelStyleKey = null;
14904 _p.nodeKey = null;
14905 _p.hasPie = null;
14906}; // apply a property to the style (for internal use)
14907// returns whether application was successful
14908//
14909// now, this function flattens the property, and here's how:
14910//
14911// for parsedProp:{ bypass: true, deleteBypass: true }
14912// no property is generated, instead the bypass property in the
14913// element's style is replaced by what's pointed to by the `bypassed`
14914// field in the bypass property (i.e. restoring the property the
14915// bypass was overriding)
14916//
14917// for parsedProp:{ mapped: truthy }
14918// the generated flattenedProp:{ mapping: prop }
14919//
14920// for parsedProp:{ bypass: true }
14921// the generated flattenedProp:{ bypassed: parsedProp }
14922
14923
14924styfn.applyParsedProperty = function (ele, parsedProp) {
14925 var self = this;
14926 var prop = parsedProp;
14927 var style = ele._private.style;
14928 var flatProp;
14929 var types = self.types;
14930 var type = self.properties[prop.name].type;
14931 var propIsBypass = prop.bypass;
14932 var origProp = style[prop.name];
14933 var origPropIsBypass = origProp && origProp.bypass;
14934 var _p = ele._private;
14935 var flatPropMapping = 'mapping';
14936
14937 var getVal = function getVal(p) {
14938 if (p == null) {
14939 return null;
14940 } else if (p.pfValue != null) {
14941 return p.pfValue;
14942 } else {
14943 return p.value;
14944 }
14945 };
14946
14947 var checkTriggers = function checkTriggers() {
14948 var fromVal = getVal(origProp);
14949 var toVal = getVal(prop);
14950 self.checkTriggers(ele, prop.name, fromVal, toVal);
14951 }; // edge sanity checks to prevent the client from making serious mistakes
14952
14953
14954 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
14955 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
14956 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
14957 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
14958 }
14959
14960 if (prop["delete"]) {
14961 // delete the property and use the default value on falsey value
14962 style[prop.name] = undefined;
14963 checkTriggers();
14964 return true;
14965 }
14966
14967 if (prop.deleteBypassed) {
14968 // delete the property that the
14969 if (!origProp) {
14970 checkTriggers();
14971 return true; // can't delete if no prop
14972 } else if (origProp.bypass) {
14973 // delete bypassed
14974 origProp.bypassed = undefined;
14975 checkTriggers();
14976 return true;
14977 } else {
14978 return false; // we're unsuccessful deleting the bypassed
14979 }
14980 } // check if we need to delete the current bypass
14981
14982
14983 if (prop.deleteBypass) {
14984 // then this property is just here to indicate we need to delete
14985 if (!origProp) {
14986 checkTriggers();
14987 return true; // property is already not defined
14988 } else if (origProp.bypass) {
14989 // then replace the bypass property with the original
14990 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
14991 style[prop.name] = origProp.bypassed;
14992 checkTriggers();
14993 return true;
14994 } else {
14995 return false; // we're unsuccessful deleting the bypass
14996 }
14997 }
14998
14999 var printMappingErr = function printMappingErr() {
15000 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');
15001 }; // put the property in the style objects
15002
15003
15004 switch (prop.mapped) {
15005 // flatten the property if mapped
15006 case types.mapData:
15007 {
15008 // flatten the field (e.g. data.foo.bar)
15009 var fields = prop.field.split('.');
15010 var fieldVal = _p.data;
15011
15012 for (var i = 0; i < fields.length && fieldVal; i++) {
15013 var field = fields[i];
15014 fieldVal = fieldVal[field];
15015 }
15016
15017 if (fieldVal == null) {
15018 printMappingErr();
15019 return false;
15020 }
15021
15022 var percent;
15023
15024 if (!number(fieldVal)) {
15025 // then don't apply and fall back on the existing style
15026 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15027 return false;
15028 } else {
15029 var fieldWidth = prop.fieldMax - prop.fieldMin;
15030
15031 if (fieldWidth === 0) {
15032 // safety check -- not strictly necessary as no props of zero range should be passed here
15033 percent = 0;
15034 } else {
15035 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15036 }
15037 } // make sure to bound percent value
15038
15039
15040 if (percent < 0) {
15041 percent = 0;
15042 } else if (percent > 1) {
15043 percent = 1;
15044 }
15045
15046 if (type.color) {
15047 var r1 = prop.valueMin[0];
15048 var r2 = prop.valueMax[0];
15049 var g1 = prop.valueMin[1];
15050 var g2 = prop.valueMax[1];
15051 var b1 = prop.valueMin[2];
15052 var b2 = prop.valueMax[2];
15053 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15054 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15055 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)];
15056 flatProp = {
15057 // colours are simple, so just create the flat property instead of expensive string parsing
15058 bypass: prop.bypass,
15059 // we're a bypass if the mapping property is a bypass
15060 name: prop.name,
15061 value: clr,
15062 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15063 };
15064 } else if (type.number) {
15065 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15066 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15067 } else {
15068 return false; // can only map to colours and numbers
15069 }
15070
15071 if (!flatProp) {
15072 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15073 printMappingErr();
15074 return false;
15075 }
15076
15077 flatProp.mapping = prop; // keep a reference to the mapping
15078
15079 prop = flatProp; // the flattened (mapped) property is the one we want
15080
15081 break;
15082 }
15083 // direct mapping
15084
15085 case types.data:
15086 {
15087 // flatten the field (e.g. data.foo.bar)
15088 var _fields = prop.field.split('.');
15089
15090 var _fieldVal = _p.data;
15091
15092 for (var _i4 = 0; _i4 < _fields.length && _fieldVal; _i4++) {
15093 var _field = _fields[_i4];
15094 _fieldVal = _fieldVal[_field];
15095 }
15096
15097 if (_fieldVal != null) {
15098 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15099 }
15100
15101 if (!flatProp) {
15102 // if we can't flatten the property, then don't apply and fall back on the existing style
15103 printMappingErr();
15104 return false;
15105 }
15106
15107 flatProp.mapping = prop; // keep a reference to the mapping
15108
15109 prop = flatProp; // the flattened (mapped) property is the one we want
15110
15111 break;
15112 }
15113
15114 case types.fn:
15115 {
15116 var fn = prop.value;
15117 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15118
15119 prop.prevFnValue = fnRetVal;
15120
15121 if (fnRetVal == null) {
15122 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15123 return false;
15124 }
15125
15126 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15127
15128 if (!flatProp) {
15129 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15130 return false;
15131 }
15132
15133 flatProp.mapping = copy(prop); // keep a reference to the mapping
15134
15135 prop = flatProp; // the flattened (mapped) property is the one we want
15136
15137 break;
15138 }
15139
15140 case undefined:
15141 break;
15142 // just set the property
15143
15144 default:
15145 return false;
15146 // not a valid mapping
15147 } // if the property is a bypass property, then link the resultant property to the original one
15148
15149
15150 if (propIsBypass) {
15151 if (origPropIsBypass) {
15152 // then this bypass overrides the existing one
15153 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15154 } else {
15155 // then link the orig prop to the new bypass
15156 prop.bypassed = origProp;
15157 }
15158
15159 style[prop.name] = prop; // and set
15160 } else {
15161 // prop is not bypass
15162 if (origPropIsBypass) {
15163 // then keep the orig prop (since it's a bypass) and link to the new prop
15164 origProp.bypassed = prop;
15165 } else {
15166 // then just replace the old prop with the new one
15167 style[prop.name] = prop;
15168 }
15169 }
15170
15171 checkTriggers();
15172 return true;
15173};
15174
15175styfn.cleanElements = function (eles, keepBypasses) {
15176 for (var i = 0; i < eles.length; i++) {
15177 var ele = eles[i];
15178 this.clearStyleHints(ele);
15179 ele.dirtyCompoundBoundsCache();
15180 ele.dirtyBoundingBoxCache();
15181
15182 if (!keepBypasses) {
15183 ele._private.style = {};
15184 } else {
15185 var style = ele._private.style;
15186 var propNames = Object.keys(style);
15187
15188 for (var j = 0; j < propNames.length; j++) {
15189 var propName = propNames[j];
15190 var eleProp = style[propName];
15191
15192 if (eleProp != null) {
15193 if (eleProp.bypass) {
15194 eleProp.bypassed = null;
15195 } else {
15196 style[propName] = null;
15197 }
15198 }
15199 }
15200 }
15201 }
15202}; // updates the visual style for all elements (useful for manual style modification after init)
15203
15204
15205styfn.update = function () {
15206 var cy = this._private.cy;
15207 var eles = cy.mutableElements();
15208 eles.updateStyle();
15209}; // diffProps : { name => { prev, next } }
15210
15211
15212styfn.updateTransitions = function (ele, diffProps) {
15213 var self = this;
15214 var _p = ele._private;
15215 var props = ele.pstyle('transition-property').value;
15216 var duration = ele.pstyle('transition-duration').pfValue;
15217 var delay = ele.pstyle('transition-delay').pfValue;
15218
15219 if (props.length > 0 && duration > 0) {
15220 var style = {}; // build up the style to animate towards
15221
15222 var anyPrev = false;
15223
15224 for (var i = 0; i < props.length; i++) {
15225 var prop = props[i];
15226 var styProp = ele.pstyle(prop);
15227 var diffProp = diffProps[prop];
15228
15229 if (!diffProp) {
15230 continue;
15231 }
15232
15233 var prevProp = diffProp.prev;
15234 var fromProp = prevProp;
15235 var toProp = diffProp.next != null ? diffProp.next : styProp;
15236 var diff = false;
15237 var initVal = void 0;
15238 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15239
15240 if (!fromProp) {
15241 continue;
15242 } // consider px values
15243
15244
15245 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
15246 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15247
15248 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
15249 } else if (number(fromProp.value) && number(toProp.value)) {
15250 diff = toProp.value - fromProp.value; // nonzero is truthy
15251
15252 initVal = fromProp.value + initDt * diff; // consider colour values
15253 } else if (array(fromProp.value) && array(toProp.value)) {
15254 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15255 initVal = fromProp.strValue;
15256 } // the previous value is good for an animation only if it's different
15257
15258
15259 if (diff) {
15260 style[prop] = toProp.strValue; // to val
15261
15262 this.applyBypass(ele, prop, initVal); // from val
15263
15264 anyPrev = true;
15265 }
15266 } // end if props allow ani
15267 // can't transition if there's nothing previous to transition from
15268
15269
15270 if (!anyPrev) {
15271 return;
15272 }
15273
15274 _p.transitioning = true;
15275 new Promise$1(function (resolve) {
15276 if (delay > 0) {
15277 ele.delayAnimation(delay).play().promise().then(resolve);
15278 } else {
15279 resolve();
15280 }
15281 }).then(function () {
15282 return ele.animation({
15283 style: style,
15284 duration: duration,
15285 easing: ele.pstyle('transition-timing-function').value,
15286 queue: false
15287 }).play().promise();
15288 }).then(function () {
15289 // if( !isBypass ){
15290 self.removeBypasses(ele, props);
15291 ele.emitAndNotify('style'); // }
15292
15293 _p.transitioning = false;
15294 });
15295 } else if (_p.transitioning) {
15296 this.removeBypasses(ele, props);
15297 ele.emitAndNotify('style');
15298 _p.transitioning = false;
15299 }
15300};
15301
15302styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15303 var prop = this.properties[name];
15304 var triggerCheck = getTrigger(prop);
15305
15306 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15307 onTrigger(prop);
15308 }
15309};
15310
15311styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15312 var _this = this;
15313
15314 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15315 return prop.triggersZOrder;
15316 }, function () {
15317 _this._private.cy.notify('zorder', ele);
15318 });
15319};
15320
15321styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15322 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15323 return prop.triggersBounds;
15324 }, function (prop) {
15325 ele.dirtyCompoundBoundsCache();
15326 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
15327 // then dirty the pll edge bb cache as well
15328
15329 if ( // only for beziers -- so performance of other edges isn't affected
15330 (ele.pstyle('curve-style').value === 'bezier' // already a bezier
15331 // was just now changed to or from a bezier:
15332 || name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier')) && prop.triggersBoundsOfParallelBeziers) {
15333 ele.parallelEdges().forEach(function (pllEdge) {
15334 if (pllEdge.isBundledBezier()) {
15335 pllEdge.dirtyBoundingBoxCache();
15336 }
15337 });
15338 }
15339 });
15340};
15341
15342styfn.checkTriggers = function (ele, name, fromValue, toValue) {
15343 ele.dirtyStyleCache();
15344 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15345 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15346};
15347
15348var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
15349// returns true iff application was successful for at least 1 specified property
15350
15351styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
15352 var self = this;
15353 var props = [];
15354 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
15355
15356 if (name === '*' || name === '**') {
15357 // apply to all property names
15358 if (value !== undefined) {
15359 for (var i = 0; i < self.properties.length; i++) {
15360 var prop = self.properties[i];
15361 var _name = prop.name;
15362 var parsedProp = this.parse(_name, value, true);
15363
15364 if (parsedProp) {
15365 props.push(parsedProp);
15366 }
15367 }
15368 }
15369 } else if (string(name)) {
15370 // then parse the single property
15371 var _parsedProp = this.parse(name, value, true);
15372
15373 if (_parsedProp) {
15374 props.push(_parsedProp);
15375 }
15376 } else if (plainObject(name)) {
15377 // then parse each property
15378 var specifiedProps = name;
15379 updateTransitions = value;
15380 var names = Object.keys(specifiedProps);
15381
15382 for (var _i = 0; _i < names.length; _i++) {
15383 var _name2 = names[_i];
15384 var _value = specifiedProps[_name2];
15385
15386 if (_value === undefined) {
15387 // try camel case name too
15388 _value = specifiedProps[dash2camel(_name2)];
15389 }
15390
15391 if (_value !== undefined) {
15392 var _parsedProp2 = this.parse(_name2, _value, true);
15393
15394 if (_parsedProp2) {
15395 props.push(_parsedProp2);
15396 }
15397 }
15398 }
15399 } else {
15400 // can't do anything without well defined properties
15401 return false;
15402 } // we've failed if there are no valid properties
15403
15404
15405 if (props.length === 0) {
15406 return false;
15407 } // now, apply the bypass properties on the elements
15408
15409
15410 var ret = false; // return true if at least one succesful bypass applied
15411
15412 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15413 // for each ele
15414 var ele = eles[_i2];
15415 var diffProps = {};
15416 var diffProp = void 0;
15417
15418 for (var j = 0; j < props.length; j++) {
15419 // for each prop
15420 var _prop = props[j];
15421
15422 if (updateTransitions) {
15423 var prevProp = ele.pstyle(_prop.name);
15424 diffProp = diffProps[_prop.name] = {
15425 prev: prevProp
15426 };
15427 }
15428
15429 ret = this.applyParsedProperty(ele, _prop) || ret;
15430
15431 if (updateTransitions) {
15432 diffProp.next = ele.pstyle(_prop.name);
15433 }
15434 } // for props
15435
15436
15437 if (ret) {
15438 this.updateStyleHints(ele);
15439 }
15440
15441 if (updateTransitions) {
15442 this.updateTransitions(ele, diffProps, isBypass);
15443 }
15444 } // for eles
15445
15446
15447 return ret;
15448}; // only useful in specific cases like animation
15449
15450
15451styfn$1.overrideBypass = function (eles, name, value) {
15452 name = camel2dash(name);
15453
15454 for (var i = 0; i < eles.length; i++) {
15455 var ele = eles[i];
15456 var prop = ele._private.style[name];
15457 var type = this.properties[name].type;
15458 var isColor = type.color;
15459 var isMulti = type.mutiple;
15460 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15461
15462 if (!prop || !prop.bypass) {
15463 // need a bypass if one doesn't exist
15464 this.applyBypass(ele, name, value);
15465 } else {
15466 prop.value = value;
15467
15468 if (prop.pfValue != null) {
15469 prop.pfValue = value;
15470 }
15471
15472 if (isColor) {
15473 prop.strValue = 'rgb(' + value.join(',') + ')';
15474 } else if (isMulti) {
15475 prop.strValue = value.join(' ');
15476 } else {
15477 prop.strValue = '' + value;
15478 }
15479
15480 this.updateStyleHints(ele);
15481 }
15482
15483 this.checkTriggers(ele, name, oldValue, value);
15484 }
15485};
15486
15487styfn$1.removeAllBypasses = function (eles, updateTransitions) {
15488 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15489};
15490
15491styfn$1.removeBypasses = function (eles, props, updateTransitions) {
15492 var isBypass = true;
15493
15494 for (var j = 0; j < eles.length; j++) {
15495 var ele = eles[j];
15496 var diffProps = {};
15497
15498 for (var i = 0; i < props.length; i++) {
15499 var name = props[i];
15500 var prop = this.properties[name];
15501 var prevProp = ele.pstyle(prop.name);
15502
15503 if (!prevProp || !prevProp.bypass) {
15504 // if a bypass doesn't exist for the prop, nothing needs to be removed
15505 continue;
15506 }
15507
15508 var value = ''; // empty => remove bypass
15509
15510 var parsedProp = this.parse(name, value, true);
15511 var diffProp = diffProps[prop.name] = {
15512 prev: prevProp
15513 };
15514 this.applyParsedProperty(ele, parsedProp);
15515 diffProp.next = ele.pstyle(prop.name);
15516 } // for props
15517
15518
15519 this.updateStyleHints(ele);
15520
15521 if (updateTransitions) {
15522 this.updateTransitions(ele, diffProps, isBypass);
15523 }
15524 } // for eles
15525
15526};
15527
15528var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
15529
15530styfn$2.getEmSizeInPixels = function () {
15531 var px = this.containerCss('font-size');
15532
15533 if (px != null) {
15534 return parseFloat(px);
15535 } else {
15536 return 1; // for headless
15537 }
15538}; // gets css property from the core container
15539
15540
15541styfn$2.containerCss = function (propName) {
15542 var cy = this._private.cy;
15543 var domElement = cy.container();
15544
15545 if (window$1 && domElement && window$1.getComputedStyle) {
15546 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
15547 }
15548};
15549
15550var styfn$3 = {}; // gets the rendered style for an element
15551
15552styfn$3.getRenderedStyle = function (ele, prop) {
15553 if (prop) {
15554 return this.getStylePropertyValue(ele, prop, true);
15555 } else {
15556 return this.getRawStyle(ele, true);
15557 }
15558}; // gets the raw style for an element
15559
15560
15561styfn$3.getRawStyle = function (ele, isRenderedVal) {
15562 var self = this;
15563 ele = ele[0]; // insure it's an element
15564
15565 if (ele) {
15566 var rstyle = {};
15567
15568 for (var i = 0; i < self.properties.length; i++) {
15569 var prop = self.properties[i];
15570 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15571
15572 if (val != null) {
15573 rstyle[prop.name] = val;
15574 rstyle[dash2camel(prop.name)] = val;
15575 }
15576 }
15577
15578 return rstyle;
15579 }
15580};
15581
15582styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
15583 var pstyle = ele.pstyle(property)[subproperty][index];
15584 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15585};
15586
15587styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15588 var self = this;
15589 ele = ele[0]; // insure it's an element
15590
15591 if (ele) {
15592 var prop = self.properties[propName];
15593
15594 if (prop.alias) {
15595 prop = prop.pointsTo;
15596 }
15597
15598 var type = prop.type;
15599 var styleProp = ele.pstyle(prop.name);
15600
15601 if (styleProp) {
15602 var value = styleProp.value,
15603 units = styleProp.units,
15604 strValue = styleProp.strValue;
15605
15606 if (isRenderedVal && type.number && value != null && number(value)) {
15607 var zoom = ele.cy().zoom();
15608
15609 var getRenderedValue = function getRenderedValue(val) {
15610 return val * zoom;
15611 };
15612
15613 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15614 return getRenderedValue(val) + units;
15615 };
15616
15617 var isArrayValue = array(value);
15618 var haveUnits = isArrayValue ? units.every(function (u) {
15619 return u != null;
15620 }) : units != null;
15621
15622 if (haveUnits) {
15623 if (isArrayValue) {
15624 return value.map(function (v, i) {
15625 return getValueStringWithUnits(v, units[i]);
15626 }).join(' ');
15627 } else {
15628 return getValueStringWithUnits(value, units);
15629 }
15630 } else {
15631 if (isArrayValue) {
15632 return value.map(function (v) {
15633 return string(v) ? v : '' + getRenderedValue(v);
15634 }).join(' ');
15635 } else {
15636 return '' + getRenderedValue(value);
15637 }
15638 }
15639 } else if (strValue != null) {
15640 return strValue;
15641 }
15642 }
15643
15644 return null;
15645 }
15646};
15647
15648styfn$3.getAnimationStartStyle = function (ele, aniProps) {
15649 var rstyle = {};
15650
15651 for (var i = 0; i < aniProps.length; i++) {
15652 var aniProp = aniProps[i];
15653 var name = aniProp.name;
15654 var styleProp = ele.pstyle(name);
15655
15656 if (styleProp !== undefined) {
15657 // then make a prop of it
15658 if (plainObject(styleProp)) {
15659 styleProp = this.parse(name, styleProp.strValue);
15660 } else {
15661 styleProp = this.parse(name, styleProp);
15662 }
15663 }
15664
15665 if (styleProp) {
15666 rstyle[name] = styleProp;
15667 }
15668 }
15669
15670 return rstyle;
15671};
15672
15673styfn$3.getPropsList = function (propsObj) {
15674 var self = this;
15675 var rstyle = [];
15676 var style = propsObj;
15677 var props = self.properties;
15678
15679 if (style) {
15680 var names = Object.keys(style);
15681
15682 for (var i = 0; i < names.length; i++) {
15683 var name = names[i];
15684 var val = style[name];
15685 var prop = props[name] || props[camel2dash(name)];
15686 var styleProp = this.parse(prop.name, val);
15687
15688 if (styleProp) {
15689 rstyle.push(styleProp);
15690 }
15691 }
15692 }
15693
15694 return rstyle;
15695};
15696
15697styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
15698 var hash = seed;
15699 var name, val, strVal, chVal;
15700 var i, j;
15701
15702 for (i = 0; i < propNames.length; i++) {
15703 name = propNames[i];
15704 val = ele.pstyle(name, false);
15705
15706 if (val == null) {
15707 continue;
15708 } else if (val.pfValue != null) {
15709 hash = hashInt(chVal, hash);
15710 } else {
15711 strVal = val.strValue;
15712
15713 for (j = 0; j < strVal.length; j++) {
15714 chVal = strVal.charCodeAt(j);
15715 hash = hashInt(chVal, hash);
15716 }
15717 }
15718 }
15719
15720 return hash;
15721};
15722
15723styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
15724
15725var styfn$4 = {};
15726
15727styfn$4.appendFromJson = function (json) {
15728 var style = this;
15729
15730 for (var i = 0; i < json.length; i++) {
15731 var context = json[i];
15732 var selector = context.selector;
15733 var props = context.style || context.css;
15734 var names = Object.keys(props);
15735 style.selector(selector); // apply selector
15736
15737 for (var j = 0; j < names.length; j++) {
15738 var name = names[j];
15739 var value = props[name];
15740 style.css(name, value); // apply property
15741 }
15742 }
15743
15744 return style;
15745}; // accessible cy.style() function
15746
15747
15748styfn$4.fromJson = function (json) {
15749 var style = this;
15750 style.resetToDefault();
15751 style.appendFromJson(json);
15752 return style;
15753}; // get json from cy.style() api
15754
15755
15756styfn$4.json = function () {
15757 var json = [];
15758
15759 for (var i = this.defaultLength; i < this.length; i++) {
15760 var cxt = this[i];
15761 var selector = cxt.selector;
15762 var props = cxt.properties;
15763 var css = {};
15764
15765 for (var j = 0; j < props.length; j++) {
15766 var prop = props[j];
15767 css[prop.name] = prop.strValue;
15768 }
15769
15770 json.push({
15771 selector: !selector ? 'core' : selector.toString(),
15772 style: css
15773 });
15774 }
15775
15776 return json;
15777};
15778
15779var styfn$5 = {};
15780
15781styfn$5.appendFromString = function (string) {
15782 var self = this;
15783 var style = this;
15784 var remaining = '' + string;
15785 var selAndBlockStr;
15786 var blockRem;
15787 var propAndValStr; // remove comments from the style string
15788
15789 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
15790
15791 function removeSelAndBlockFromRemaining() {
15792 // remove the parsed selector and block from the remaining text to parse
15793 if (remaining.length > selAndBlockStr.length) {
15794 remaining = remaining.substr(selAndBlockStr.length);
15795 } else {
15796 remaining = '';
15797 }
15798 }
15799
15800 function removePropAndValFromRem() {
15801 // remove the parsed property and value from the remaining block text to parse
15802 if (blockRem.length > propAndValStr.length) {
15803 blockRem = blockRem.substr(propAndValStr.length);
15804 } else {
15805 blockRem = '';
15806 }
15807 }
15808
15809 for (;;) {
15810 var nothingLeftToParse = remaining.match(/^\s*$/);
15811
15812 if (nothingLeftToParse) {
15813 break;
15814 }
15815
15816 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
15817
15818 if (!selAndBlock) {
15819 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
15820 break;
15821 }
15822
15823 selAndBlockStr = selAndBlock[0]; // parse the selector
15824
15825 var selectorStr = selAndBlock[1];
15826
15827 if (selectorStr !== 'core') {
15828 var selector = new Selector(selectorStr);
15829
15830 if (selector.invalid) {
15831 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
15832
15833 removeSelAndBlockFromRemaining();
15834 continue;
15835 }
15836 } // parse the block of properties and values
15837
15838
15839 var blockStr = selAndBlock[2];
15840 var invalidBlock = false;
15841 blockRem = blockStr;
15842 var props = [];
15843
15844 for (;;) {
15845 var _nothingLeftToParse = blockRem.match(/^\s*$/);
15846
15847 if (_nothingLeftToParse) {
15848 break;
15849 }
15850
15851 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
15852
15853 if (!propAndVal) {
15854 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
15855 invalidBlock = true;
15856 break;
15857 }
15858
15859 propAndValStr = propAndVal[0];
15860 var propStr = propAndVal[1];
15861 var valStr = propAndVal[2];
15862 var prop = self.properties[propStr];
15863
15864 if (!prop) {
15865 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
15866
15867 removePropAndValFromRem();
15868 continue;
15869 }
15870
15871 var parsedProp = style.parse(propStr, valStr);
15872
15873 if (!parsedProp) {
15874 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
15875
15876 removePropAndValFromRem();
15877 continue;
15878 }
15879
15880 props.push({
15881 name: propStr,
15882 val: valStr
15883 });
15884 removePropAndValFromRem();
15885 }
15886
15887 if (invalidBlock) {
15888 removeSelAndBlockFromRemaining();
15889 break;
15890 } // put the parsed block in the style
15891
15892
15893 style.selector(selectorStr);
15894
15895 for (var i = 0; i < props.length; i++) {
15896 var _prop = props[i];
15897 style.css(_prop.name, _prop.val);
15898 }
15899
15900 removeSelAndBlockFromRemaining();
15901 }
15902
15903 return style;
15904};
15905
15906styfn$5.fromString = function (string) {
15907 var style = this;
15908 style.resetToDefault();
15909 style.appendFromString(string);
15910 return style;
15911};
15912
15913var styfn$6 = {};
15914
15915(function () {
15916 var number = number$1;
15917 var rgba = rgbaNoBackRefs;
15918 var hsla = hslaNoBackRefs;
15919 var hex3$1 = hex3;
15920 var hex6$1 = hex6;
15921
15922 var data = function data(prefix) {
15923 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
15924 };
15925
15926 var mapData = function mapData(prefix) {
15927 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
15928 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
15929 };
15930
15931 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
15932
15933 styfn$6.types = {
15934 time: {
15935 number: true,
15936 min: 0,
15937 units: 's|ms',
15938 implicitUnits: 'ms'
15939 },
15940 percent: {
15941 number: true,
15942 min: 0,
15943 max: 100,
15944 units: '%',
15945 implicitUnits: '%'
15946 },
15947 percentages: {
15948 number: true,
15949 min: 0,
15950 max: 100,
15951 units: '%',
15952 implicitUnits: '%',
15953 multiple: true
15954 },
15955 zeroOneNumber: {
15956 number: true,
15957 min: 0,
15958 max: 1,
15959 unitless: true
15960 },
15961 zeroOneNumbers: {
15962 number: true,
15963 min: 0,
15964 max: 1,
15965 unitless: true,
15966 multiple: true
15967 },
15968 nOneOneNumber: {
15969 number: true,
15970 min: -1,
15971 max: 1,
15972 unitless: true
15973 },
15974 nonNegativeInt: {
15975 number: true,
15976 min: 0,
15977 integer: true,
15978 unitless: true
15979 },
15980 position: {
15981 enums: ['parent', 'origin']
15982 },
15983 nodeSize: {
15984 number: true,
15985 min: 0,
15986 enums: ['label']
15987 },
15988 number: {
15989 number: true,
15990 unitless: true
15991 },
15992 numbers: {
15993 number: true,
15994 unitless: true,
15995 multiple: true
15996 },
15997 positiveNumber: {
15998 number: true,
15999 unitless: true,
16000 min: 0,
16001 strictMin: true
16002 },
16003 size: {
16004 number: true,
16005 min: 0
16006 },
16007 bidirectionalSize: {
16008 number: true
16009 },
16010 // allows negative
16011 bidirectionalSizeMaybePercent: {
16012 number: true,
16013 allowPercent: true
16014 },
16015 // allows negative
16016 bidirectionalSizes: {
16017 number: true,
16018 multiple: true
16019 },
16020 // allows negative
16021 sizeMaybePercent: {
16022 number: true,
16023 min: 0,
16024 allowPercent: true
16025 },
16026 axisDirection: {
16027 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16028 },
16029 paddingRelativeTo: {
16030 enums: ['width', 'height', 'average', 'min', 'max']
16031 },
16032 bgWH: {
16033 number: true,
16034 min: 0,
16035 allowPercent: true,
16036 enums: ['auto'],
16037 multiple: true
16038 },
16039 bgPos: {
16040 number: true,
16041 allowPercent: true,
16042 multiple: true
16043 },
16044 bgRelativeTo: {
16045 enums: ['inner', 'include-padding'],
16046 multiple: true
16047 },
16048 bgRepeat: {
16049 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16050 multiple: true
16051 },
16052 bgFit: {
16053 enums: ['none', 'contain', 'cover'],
16054 multiple: true
16055 },
16056 bgCrossOrigin: {
16057 enums: ['anonymous', 'use-credentials'],
16058 multiple: true
16059 },
16060 bgClip: {
16061 enums: ['none', 'node'],
16062 multiple: true
16063 },
16064 color: {
16065 color: true
16066 },
16067 colors: {
16068 color: true,
16069 multiple: true
16070 },
16071 fill: {
16072 enums: ['solid', 'linear-gradient', 'radial-gradient']
16073 },
16074 bool: {
16075 enums: ['yes', 'no']
16076 },
16077 lineStyle: {
16078 enums: ['solid', 'dotted', 'dashed']
16079 },
16080 lineCap: {
16081 enums: ['butt', 'round', 'square']
16082 },
16083 borderStyle: {
16084 enums: ['solid', 'dotted', 'dashed', 'double']
16085 },
16086 curveStyle: {
16087 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
16088 },
16089 fontFamily: {
16090 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16091 },
16092 fontStyle: {
16093 enums: ['italic', 'normal', 'oblique']
16094 },
16095 fontWeight: {
16096 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16097 },
16098 textDecoration: {
16099 enums: ['none', 'underline', 'overline', 'line-through']
16100 },
16101 textTransform: {
16102 enums: ['none', 'uppercase', 'lowercase']
16103 },
16104 textWrap: {
16105 enums: ['none', 'wrap', 'ellipsis']
16106 },
16107 textOverflowWrap: {
16108 enums: ['whitespace', 'anywhere']
16109 },
16110 textBackgroundShape: {
16111 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16112 },
16113 nodeShape: {
16114 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']
16115 },
16116 compoundIncludeLabels: {
16117 enums: ['include', 'exclude']
16118 },
16119 arrowShape: {
16120 enums: ['tee', 'triangle', 'triangle-tee', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16121 },
16122 arrowFill: {
16123 enums: ['filled', 'hollow']
16124 },
16125 display: {
16126 enums: ['element', 'none']
16127 },
16128 visibility: {
16129 enums: ['hidden', 'visible']
16130 },
16131 zCompoundDepth: {
16132 enums: ['bottom', 'orphan', 'auto', 'top']
16133 },
16134 zIndexCompare: {
16135 enums: ['auto', 'manual']
16136 },
16137 valign: {
16138 enums: ['top', 'center', 'bottom']
16139 },
16140 halign: {
16141 enums: ['left', 'center', 'right']
16142 },
16143 justification: {
16144 enums: ['left', 'center', 'right', 'auto']
16145 },
16146 text: {
16147 string: true
16148 },
16149 data: {
16150 mapping: true,
16151 regex: data('data')
16152 },
16153 layoutData: {
16154 mapping: true,
16155 regex: data('layoutData')
16156 },
16157 scratch: {
16158 mapping: true,
16159 regex: data('scratch')
16160 },
16161 mapData: {
16162 mapping: true,
16163 regex: mapData('mapData')
16164 },
16165 mapLayoutData: {
16166 mapping: true,
16167 regex: mapData('mapLayoutData')
16168 },
16169 mapScratch: {
16170 mapping: true,
16171 regex: mapData('mapScratch')
16172 },
16173 fn: {
16174 mapping: true,
16175 fn: true
16176 },
16177 url: {
16178 regexes: urlRegexes,
16179 singleRegexMatchValue: true
16180 },
16181 urls: {
16182 regexes: urlRegexes,
16183 singleRegexMatchValue: true,
16184 multiple: true
16185 },
16186 propList: {
16187 propList: true
16188 },
16189 angle: {
16190 number: true,
16191 units: 'deg|rad',
16192 implicitUnits: 'rad'
16193 },
16194 textRotation: {
16195 number: true,
16196 units: 'deg|rad',
16197 implicitUnits: 'rad',
16198 enums: ['none', 'autorotate']
16199 },
16200 polygonPointList: {
16201 number: true,
16202 multiple: true,
16203 evenMultiple: true,
16204 min: -1,
16205 max: 1,
16206 unitless: true
16207 },
16208 edgeDistances: {
16209 enums: ['intersection', 'node-position']
16210 },
16211 edgeEndpoint: {
16212 number: true,
16213 multiple: true,
16214 units: '%|px|em|deg|rad',
16215 implicitUnits: 'px',
16216 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16217 singleEnum: true,
16218 validate: function validate(valArr, unitsArr) {
16219 switch (valArr.length) {
16220 case 2:
16221 // can be % or px only
16222 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16223
16224 case 1:
16225 // can be enum, deg, or rad only
16226 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16227
16228 default:
16229 return false;
16230 }
16231 }
16232 },
16233 easing: {
16234 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
16235 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']
16236 },
16237 gradientDirection: {
16238 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']
16239 },
16240 boundsExpansion: {
16241 number: true,
16242 multiple: true,
16243 min: 0,
16244 validate: function validate(valArr) {
16245 var length = valArr.length;
16246 return length === 1 || length === 2 || length === 4;
16247 }
16248 }
16249 };
16250 var diff = {
16251 zeroNonZero: function zeroNonZero(val1, val2) {
16252 if ((val1 == null || val2 == null) && val1 !== val2) {
16253 return true; // null cases could represent any value
16254 }
16255
16256 if (val1 == 0 && val2 != 0) {
16257 return true;
16258 } else if (val1 != 0 && val2 == 0) {
16259 return true;
16260 } else {
16261 return false;
16262 }
16263 },
16264 any: function any(val1, val2) {
16265 return val1 != val2;
16266 }
16267 }; // define visual style properties
16268 //
16269 // - n.b. adding a new group of props may require updates to updateStyleHints()
16270 // - adding new props to an existing group gets handled automatically
16271
16272 var t = styfn$6.types;
16273 var mainLabel = [{
16274 name: 'label',
16275 type: t.text,
16276 triggersBounds: diff.any
16277 }, {
16278 name: 'text-rotation',
16279 type: t.textRotation,
16280 triggersBounds: diff.any
16281 }, {
16282 name: 'text-margin-x',
16283 type: t.bidirectionalSize,
16284 triggersBounds: diff.any
16285 }, {
16286 name: 'text-margin-y',
16287 type: t.bidirectionalSize,
16288 triggersBounds: diff.any
16289 }];
16290 var sourceLabel = [{
16291 name: 'source-label',
16292 type: t.text,
16293 triggersBounds: diff.any
16294 }, {
16295 name: 'source-text-rotation',
16296 type: t.textRotation,
16297 triggersBounds: diff.any
16298 }, {
16299 name: 'source-text-margin-x',
16300 type: t.bidirectionalSize,
16301 triggersBounds: diff.any
16302 }, {
16303 name: 'source-text-margin-y',
16304 type: t.bidirectionalSize,
16305 triggersBounds: diff.any
16306 }, {
16307 name: 'source-text-offset',
16308 type: t.size,
16309 triggersBounds: diff.any
16310 }];
16311 var targetLabel = [{
16312 name: 'target-label',
16313 type: t.text,
16314 triggersBounds: diff.any
16315 }, {
16316 name: 'target-text-rotation',
16317 type: t.textRotation,
16318 triggersBounds: diff.any
16319 }, {
16320 name: 'target-text-margin-x',
16321 type: t.bidirectionalSize,
16322 triggersBounds: diff.any
16323 }, {
16324 name: 'target-text-margin-y',
16325 type: t.bidirectionalSize,
16326 triggersBounds: diff.any
16327 }, {
16328 name: 'target-text-offset',
16329 type: t.size,
16330 triggersBounds: diff.any
16331 }];
16332 var labelDimensions = [{
16333 name: 'font-family',
16334 type: t.fontFamily,
16335 triggersBounds: diff.any
16336 }, {
16337 name: 'font-style',
16338 type: t.fontStyle,
16339 triggersBounds: diff.any
16340 }, {
16341 name: 'font-weight',
16342 type: t.fontWeight,
16343 triggersBounds: diff.any
16344 }, {
16345 name: 'font-size',
16346 type: t.size,
16347 triggersBounds: diff.any
16348 }, {
16349 name: 'text-transform',
16350 type: t.textTransform,
16351 triggersBounds: diff.any
16352 }, {
16353 name: 'text-wrap',
16354 type: t.textWrap,
16355 triggersBounds: diff.any
16356 }, {
16357 name: 'text-overflow-wrap',
16358 type: t.textOverflowWrap,
16359 triggersBounds: diff.any
16360 }, {
16361 name: 'text-max-width',
16362 type: t.size,
16363 triggersBounds: diff.any
16364 }, {
16365 name: 'text-outline-width',
16366 type: t.size,
16367 triggersBounds: diff.any
16368 }, {
16369 name: 'line-height',
16370 type: t.positiveNumber,
16371 triggersBounds: diff.any
16372 }];
16373 var commonLabel = [{
16374 name: 'text-valign',
16375 type: t.valign,
16376 triggersBounds: diff.any
16377 }, {
16378 name: 'text-halign',
16379 type: t.halign,
16380 triggersBounds: diff.any
16381 }, {
16382 name: 'color',
16383 type: t.color
16384 }, {
16385 name: 'text-outline-color',
16386 type: t.color
16387 }, {
16388 name: 'text-outline-opacity',
16389 type: t.zeroOneNumber
16390 }, {
16391 name: 'text-background-color',
16392 type: t.color
16393 }, {
16394 name: 'text-background-opacity',
16395 type: t.zeroOneNumber
16396 }, {
16397 name: 'text-background-padding',
16398 type: t.size,
16399 triggersBounds: diff.any
16400 }, {
16401 name: 'text-border-opacity',
16402 type: t.zeroOneNumber
16403 }, {
16404 name: 'text-border-color',
16405 type: t.color
16406 }, {
16407 name: 'text-border-width',
16408 type: t.size,
16409 triggersBounds: diff.any
16410 }, {
16411 name: 'text-border-style',
16412 type: t.borderStyle,
16413 triggersBounds: diff.any
16414 }, {
16415 name: 'text-background-shape',
16416 type: t.textBackgroundShape,
16417 triggersBounds: diff.any
16418 }, {
16419 name: 'text-justification',
16420 type: t.justification
16421 }];
16422 var behavior = [{
16423 name: 'events',
16424 type: t.bool
16425 }, {
16426 name: 'text-events',
16427 type: t.bool
16428 }];
16429 var visibility = [{
16430 name: 'display',
16431 type: t.display,
16432 triggersZOrder: diff.any,
16433 triggersBounds: diff.any,
16434 triggersBoundsOfParallelBeziers: true
16435 }, {
16436 name: 'visibility',
16437 type: t.visibility,
16438 triggersZOrder: diff.any
16439 }, {
16440 name: 'opacity',
16441 type: t.zeroOneNumber,
16442 triggersZOrder: diff.zeroNonZero
16443 }, {
16444 name: 'text-opacity',
16445 type: t.zeroOneNumber
16446 }, {
16447 name: 'min-zoomed-font-size',
16448 type: t.size
16449 }, {
16450 name: 'z-compound-depth',
16451 type: t.zCompoundDepth,
16452 triggersZOrder: diff.any
16453 }, {
16454 name: 'z-index-compare',
16455 type: t.zIndexCompare,
16456 triggersZOrder: diff.any
16457 }, {
16458 name: 'z-index',
16459 type: t.nonNegativeInt,
16460 triggersZOrder: diff.any
16461 }];
16462 var overlay = [{
16463 name: 'overlay-padding',
16464 type: t.size,
16465 triggersBounds: diff.any
16466 }, {
16467 name: 'overlay-color',
16468 type: t.color
16469 }, {
16470 name: 'overlay-opacity',
16471 type: t.zeroOneNumber,
16472 triggersBounds: diff.zeroNonZero
16473 }];
16474 var transition = [{
16475 name: 'transition-property',
16476 type: t.propList
16477 }, {
16478 name: 'transition-duration',
16479 type: t.time
16480 }, {
16481 name: 'transition-delay',
16482 type: t.time
16483 }, {
16484 name: 'transition-timing-function',
16485 type: t.easing
16486 }];
16487
16488 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16489 if (parsedProp.value === 'label') {
16490 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16491 } else {
16492 return parsedProp.pfValue;
16493 }
16494 };
16495
16496 var nodeBody = [{
16497 name: 'height',
16498 type: t.nodeSize,
16499 triggersBounds: diff.any,
16500 hashOverride: nodeSizeHashOverride
16501 }, {
16502 name: 'width',
16503 type: t.nodeSize,
16504 triggersBounds: diff.any,
16505 hashOverride: nodeSizeHashOverride
16506 }, {
16507 name: 'shape',
16508 type: t.nodeShape,
16509 triggersBounds: diff.any
16510 }, {
16511 name: 'shape-polygon-points',
16512 type: t.polygonPointList,
16513 triggersBounds: diff.any
16514 }, {
16515 name: 'background-color',
16516 type: t.color
16517 }, {
16518 name: 'background-fill',
16519 type: t.fill
16520 }, {
16521 name: 'background-opacity',
16522 type: t.zeroOneNumber
16523 }, {
16524 name: 'background-blacken',
16525 type: t.nOneOneNumber
16526 }, {
16527 name: 'background-gradient-stop-colors',
16528 type: t.colors
16529 }, {
16530 name: 'background-gradient-stop-positions',
16531 type: t.percentages
16532 }, {
16533 name: 'background-gradient-direction',
16534 type: t.gradientDirection
16535 }, {
16536 name: 'padding',
16537 type: t.sizeMaybePercent,
16538 triggersBounds: diff.any
16539 }, {
16540 name: 'padding-relative-to',
16541 type: t.paddingRelativeTo,
16542 triggersBounds: diff.any
16543 }, {
16544 name: 'bounds-expansion',
16545 type: t.boundsExpansion,
16546 triggersBounds: diff.any
16547 }];
16548 var nodeBorder = [{
16549 name: 'border-color',
16550 type: t.color
16551 }, {
16552 name: 'border-opacity',
16553 type: t.zeroOneNumber
16554 }, {
16555 name: 'border-width',
16556 type: t.size,
16557 triggersBounds: diff.any
16558 }, {
16559 name: 'border-style',
16560 type: t.borderStyle
16561 }];
16562 var backgroundImage = [{
16563 name: 'background-image',
16564 type: t.urls
16565 }, {
16566 name: 'background-image-crossorigin',
16567 type: t.bgCrossOrigin
16568 }, {
16569 name: 'background-image-opacity',
16570 type: t.zeroOneNumbers
16571 }, {
16572 name: 'background-position-x',
16573 type: t.bgPos
16574 }, {
16575 name: 'background-position-y',
16576 type: t.bgPos
16577 }, {
16578 name: 'background-width-relative-to',
16579 type: t.bgRelativeTo
16580 }, {
16581 name: 'background-height-relative-to',
16582 type: t.bgRelativeTo
16583 }, {
16584 name: 'background-repeat',
16585 type: t.bgRepeat
16586 }, {
16587 name: 'background-fit',
16588 type: t.bgFit
16589 }, {
16590 name: 'background-clip',
16591 type: t.bgClip
16592 }, {
16593 name: 'background-width',
16594 type: t.bgWH
16595 }, {
16596 name: 'background-height',
16597 type: t.bgWH
16598 }, {
16599 name: 'background-offset-x',
16600 type: t.bgPos
16601 }, {
16602 name: 'background-offset-y',
16603 type: t.bgPos
16604 }];
16605 var compound = [{
16606 name: 'position',
16607 type: t.position,
16608 triggersBounds: diff.any
16609 }, {
16610 name: 'compound-sizing-wrt-labels',
16611 type: t.compoundIncludeLabels,
16612 triggersBounds: diff.any
16613 }, {
16614 name: 'min-width',
16615 type: t.size,
16616 triggersBounds: diff.any
16617 }, {
16618 name: 'min-width-bias-left',
16619 type: t.sizeMaybePercent,
16620 triggersBounds: diff.any
16621 }, {
16622 name: 'min-width-bias-right',
16623 type: t.sizeMaybePercent,
16624 triggersBounds: diff.any
16625 }, {
16626 name: 'min-height',
16627 type: t.size,
16628 triggersBounds: diff.any
16629 }, {
16630 name: 'min-height-bias-top',
16631 type: t.sizeMaybePercent,
16632 triggersBounds: diff.any
16633 }, {
16634 name: 'min-height-bias-bottom',
16635 type: t.sizeMaybePercent,
16636 triggersBounds: diff.any
16637 }];
16638 var edgeLine = [{
16639 name: 'line-style',
16640 type: t.lineStyle
16641 }, {
16642 name: 'line-color',
16643 type: t.color
16644 }, {
16645 name: 'line-fill',
16646 type: t.fill
16647 }, {
16648 name: 'line-cap',
16649 type: t.lineCap
16650 }, {
16651 name: 'line-dash-pattern',
16652 type: t.numbers
16653 }, {
16654 name: 'line-dash-offset',
16655 type: t.number
16656 }, {
16657 name: 'line-gradient-stop-colors',
16658 type: t.colors
16659 }, {
16660 name: 'line-gradient-stop-positions',
16661 type: t.percentages
16662 }, {
16663 name: 'curve-style',
16664 type: t.curveStyle,
16665 triggersBounds: diff.any,
16666 triggersBoundsOfParallelBeziers: true
16667 }, {
16668 name: 'haystack-radius',
16669 type: t.zeroOneNumber,
16670 triggersBounds: diff.any
16671 }, {
16672 name: 'source-endpoint',
16673 type: t.edgeEndpoint,
16674 triggersBounds: diff.any
16675 }, {
16676 name: 'target-endpoint',
16677 type: t.edgeEndpoint,
16678 triggersBounds: diff.any
16679 }, {
16680 name: 'control-point-step-size',
16681 type: t.size,
16682 triggersBounds: diff.any
16683 }, {
16684 name: 'control-point-distances',
16685 type: t.bidirectionalSizes,
16686 triggersBounds: diff.any
16687 }, {
16688 name: 'control-point-weights',
16689 type: t.numbers,
16690 triggersBounds: diff.any
16691 }, {
16692 name: 'segment-distances',
16693 type: t.bidirectionalSizes,
16694 triggersBounds: diff.any
16695 }, {
16696 name: 'segment-weights',
16697 type: t.numbers,
16698 triggersBounds: diff.any
16699 }, {
16700 name: 'taxi-turn',
16701 type: t.bidirectionalSizeMaybePercent,
16702 triggersBounds: diff.any
16703 }, {
16704 name: 'taxi-turn-min-distance',
16705 type: t.size,
16706 triggersBounds: diff.any
16707 }, {
16708 name: 'taxi-direction',
16709 type: t.axisDirection,
16710 triggersBounds: diff.any
16711 }, {
16712 name: 'edge-distances',
16713 type: t.edgeDistances,
16714 triggersBounds: diff.any
16715 }, {
16716 name: 'arrow-scale',
16717 type: t.positiveNumber,
16718 triggersBounds: diff.any
16719 }, {
16720 name: 'loop-direction',
16721 type: t.angle,
16722 triggersBounds: diff.any
16723 }, {
16724 name: 'loop-sweep',
16725 type: t.angle,
16726 triggersBounds: diff.any
16727 }, {
16728 name: 'source-distance-from-node',
16729 type: t.size,
16730 triggersBounds: diff.any
16731 }, {
16732 name: 'target-distance-from-node',
16733 type: t.size,
16734 triggersBounds: diff.any
16735 }];
16736 var ghost = [{
16737 name: 'ghost',
16738 type: t.bool,
16739 triggersBounds: diff.any
16740 }, {
16741 name: 'ghost-offset-x',
16742 type: t.bidirectionalSize,
16743 triggersBounds: diff.any
16744 }, {
16745 name: 'ghost-offset-y',
16746 type: t.bidirectionalSize,
16747 triggersBounds: diff.any
16748 }, {
16749 name: 'ghost-opacity',
16750 type: t.zeroOneNumber
16751 }];
16752 var core = [{
16753 name: 'selection-box-color',
16754 type: t.color
16755 }, {
16756 name: 'selection-box-opacity',
16757 type: t.zeroOneNumber
16758 }, {
16759 name: 'selection-box-border-color',
16760 type: t.color
16761 }, {
16762 name: 'selection-box-border-width',
16763 type: t.size
16764 }, {
16765 name: 'active-bg-color',
16766 type: t.color
16767 }, {
16768 name: 'active-bg-opacity',
16769 type: t.zeroOneNumber
16770 }, {
16771 name: 'active-bg-size',
16772 type: t.size
16773 }, {
16774 name: 'outside-texture-bg-color',
16775 type: t.color
16776 }, {
16777 name: 'outside-texture-bg-opacity',
16778 type: t.zeroOneNumber
16779 }]; // pie backgrounds for nodes
16780
16781 var pie = [];
16782 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
16783
16784 pie.push({
16785 name: 'pie-size',
16786 type: t.sizeMaybePercent
16787 });
16788
16789 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
16790 pie.push({
16791 name: 'pie-' + i + '-background-color',
16792 type: t.color
16793 });
16794 pie.push({
16795 name: 'pie-' + i + '-background-size',
16796 type: t.percent
16797 });
16798 pie.push({
16799 name: 'pie-' + i + '-background-opacity',
16800 type: t.zeroOneNumber
16801 });
16802 } // edge arrows
16803
16804
16805 var edgeArrow = [];
16806 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
16807 [{
16808 name: 'arrow-shape',
16809 type: t.arrowShape,
16810 triggersBounds: diff.any
16811 }, {
16812 name: 'arrow-color',
16813 type: t.color
16814 }, {
16815 name: 'arrow-fill',
16816 type: t.arrowFill
16817 }].forEach(function (prop) {
16818 arrowPrefixes.forEach(function (prefix) {
16819 var name = prefix + '-' + prop.name;
16820 var type = prop.type,
16821 triggersBounds = prop.triggersBounds;
16822 edgeArrow.push({
16823 name: name,
16824 type: type,
16825 triggersBounds: triggersBounds
16826 });
16827 });
16828 }, {});
16829 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
16830 var propGroups = styfn$6.propertyGroups = {
16831 // common to all eles
16832 behavior: behavior,
16833 transition: transition,
16834 visibility: visibility,
16835 overlay: overlay,
16836 ghost: ghost,
16837 // labels
16838 commonLabel: commonLabel,
16839 labelDimensions: labelDimensions,
16840 mainLabel: mainLabel,
16841 sourceLabel: sourceLabel,
16842 targetLabel: targetLabel,
16843 // node props
16844 nodeBody: nodeBody,
16845 nodeBorder: nodeBorder,
16846 backgroundImage: backgroundImage,
16847 pie: pie,
16848 compound: compound,
16849 // edge props
16850 edgeLine: edgeLine,
16851 edgeArrow: edgeArrow,
16852 core: core
16853 };
16854 var propGroupNames = styfn$6.propertyGroupNames = {};
16855 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
16856 propGroupKeys.forEach(function (key) {
16857 propGroupNames[key] = propGroups[key].map(function (prop) {
16858 return prop.name;
16859 });
16860 propGroups[key].forEach(function (prop) {
16861 return prop.groupKey = key;
16862 });
16863 }); // define aliases
16864
16865 var aliases = styfn$6.aliases = [{
16866 name: 'content',
16867 pointsTo: 'label'
16868 }, {
16869 name: 'control-point-distance',
16870 pointsTo: 'control-point-distances'
16871 }, {
16872 name: 'control-point-weight',
16873 pointsTo: 'control-point-weights'
16874 }, {
16875 name: 'edge-text-rotation',
16876 pointsTo: 'text-rotation'
16877 }, {
16878 name: 'padding-left',
16879 pointsTo: 'padding'
16880 }, {
16881 name: 'padding-right',
16882 pointsTo: 'padding'
16883 }, {
16884 name: 'padding-top',
16885 pointsTo: 'padding'
16886 }, {
16887 name: 'padding-bottom',
16888 pointsTo: 'padding'
16889 }]; // list of property names
16890
16891 styfn$6.propertyNames = props.map(function (p) {
16892 return p.name;
16893 }); // allow access of properties by name ( e.g. style.properties.height )
16894
16895 for (var _i = 0; _i < props.length; _i++) {
16896 var prop = props[_i];
16897 props[prop.name] = prop; // allow lookup by name
16898 } // map aliases
16899
16900
16901 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
16902 var alias = aliases[_i2];
16903 var pointsToProp = props[alias.pointsTo];
16904 var aliasProp = {
16905 name: alias.name,
16906 alias: true,
16907 pointsTo: pointsToProp
16908 }; // add alias prop for parsing
16909
16910 props.push(aliasProp);
16911 props[alias.name] = aliasProp; // allow lookup by name
16912 }
16913})();
16914
16915styfn$6.getDefaultProperty = function (name) {
16916 return this.getDefaultProperties()[name];
16917};
16918
16919styfn$6.getDefaultProperties = function () {
16920 var _p = this._private;
16921
16922 if (_p.defaultProperties != null) {
16923 return _p.defaultProperties;
16924 }
16925
16926 var rawProps = extend({
16927 // core props
16928 'selection-box-color': '#ddd',
16929 'selection-box-opacity': 0.65,
16930 'selection-box-border-color': '#aaa',
16931 'selection-box-border-width': 1,
16932 'active-bg-color': 'black',
16933 'active-bg-opacity': 0.15,
16934 'active-bg-size': 30,
16935 'outside-texture-bg-color': '#000',
16936 'outside-texture-bg-opacity': 0.125,
16937 // common node/edge props
16938 'events': 'yes',
16939 'text-events': 'no',
16940 'text-valign': 'top',
16941 'text-halign': 'center',
16942 'text-justification': 'auto',
16943 'line-height': 1,
16944 'color': '#000',
16945 'text-outline-color': '#000',
16946 'text-outline-width': 0,
16947 'text-outline-opacity': 1,
16948 'text-opacity': 1,
16949 'text-decoration': 'none',
16950 'text-transform': 'none',
16951 'text-wrap': 'none',
16952 'text-overflow-wrap': 'whitespace',
16953 'text-max-width': 9999,
16954 'text-background-color': '#000',
16955 'text-background-opacity': 0,
16956 'text-background-shape': 'rectangle',
16957 'text-background-padding': 0,
16958 'text-border-opacity': 0,
16959 'text-border-width': 0,
16960 'text-border-style': 'solid',
16961 'text-border-color': '#000',
16962 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
16963 'font-style': 'normal',
16964 'font-weight': 'normal',
16965 'font-size': 16,
16966 'min-zoomed-font-size': 0,
16967 'text-rotation': 'none',
16968 'source-text-rotation': 'none',
16969 'target-text-rotation': 'none',
16970 'visibility': 'visible',
16971 'display': 'element',
16972 'opacity': 1,
16973 'z-compound-depth': 'auto',
16974 'z-index-compare': 'auto',
16975 'z-index': 0,
16976 'label': '',
16977 'text-margin-x': 0,
16978 'text-margin-y': 0,
16979 'source-label': '',
16980 'source-text-offset': 0,
16981 'source-text-margin-x': 0,
16982 'source-text-margin-y': 0,
16983 'target-label': '',
16984 'target-text-offset': 0,
16985 'target-text-margin-x': 0,
16986 'target-text-margin-y': 0,
16987 'overlay-opacity': 0,
16988 'overlay-color': '#000',
16989 'overlay-padding': 10,
16990 'transition-property': 'none',
16991 'transition-duration': 0,
16992 'transition-delay': 0,
16993 'transition-timing-function': 'linear',
16994 // node props
16995 'background-blacken': 0,
16996 'background-color': '#999',
16997 'background-fill': 'solid',
16998 'background-opacity': 1,
16999 'background-image': 'none',
17000 'background-image-crossorigin': 'anonymous',
17001 'background-image-opacity': 1,
17002 'background-position-x': '50%',
17003 'background-position-y': '50%',
17004 'background-offset-x': 0,
17005 'background-offset-y': 0,
17006 'background-width-relative-to': 'include-padding',
17007 'background-height-relative-to': 'include-padding',
17008 'background-repeat': 'no-repeat',
17009 'background-fit': 'none',
17010 'background-clip': 'node',
17011 'background-width': 'auto',
17012 'background-height': 'auto',
17013 'border-color': '#000',
17014 'border-opacity': 1,
17015 'border-width': 0,
17016 'border-style': 'solid',
17017 'height': 30,
17018 'width': 30,
17019 'shape': 'ellipse',
17020 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17021 'bounds-expansion': 0,
17022 // node gradient
17023 'background-gradient-direction': 'to-bottom',
17024 'background-gradient-stop-colors': '#999',
17025 'background-gradient-stop-positions': '0%',
17026 // ghost props
17027 'ghost': 'no',
17028 'ghost-offset-y': 0,
17029 'ghost-offset-x': 0,
17030 'ghost-opacity': 0,
17031 // compound props
17032 'padding': 0,
17033 'padding-relative-to': 'width',
17034 'position': 'origin',
17035 'compound-sizing-wrt-labels': 'include',
17036 'min-width': 0,
17037 'min-width-bias-left': 0,
17038 'min-width-bias-right': 0,
17039 'min-height': 0,
17040 'min-height-bias-top': 0,
17041 'min-height-bias-bottom': 0
17042 }, {
17043 // node pie bg
17044 'pie-size': '100%'
17045 }, [{
17046 name: 'pie-{{i}}-background-color',
17047 value: 'black'
17048 }, {
17049 name: 'pie-{{i}}-background-size',
17050 value: '0%'
17051 }, {
17052 name: 'pie-{{i}}-background-opacity',
17053 value: 1
17054 }].reduce(function (css, prop) {
17055 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17056 var name = prop.name.replace('{{i}}', i);
17057 var val = prop.value;
17058 css[name] = val;
17059 }
17060
17061 return css;
17062 }, {}), {
17063 // edge props
17064 'line-style': 'solid',
17065 'line-color': '#999',
17066 'line-fill': 'solid',
17067 'line-cap': 'butt',
17068 'line-gradient-stop-colors': '#999',
17069 'line-gradient-stop-positions': '0%',
17070 'control-point-step-size': 40,
17071 'control-point-weights': 0.5,
17072 'segment-weights': 0.5,
17073 'segment-distances': 20,
17074 'taxi-turn': '50%',
17075 'taxi-turn-min-distance': 10,
17076 'taxi-direction': 'auto',
17077 'edge-distances': 'intersection',
17078 'curve-style': 'haystack',
17079 'haystack-radius': 0,
17080 'arrow-scale': 1,
17081 'loop-direction': '-45deg',
17082 'loop-sweep': '-90deg',
17083 'source-distance-from-node': 0,
17084 'target-distance-from-node': 0,
17085 'source-endpoint': 'outside-to-node',
17086 'target-endpoint': 'outside-to-node',
17087 'line-dash-pattern': [6, 3],
17088 'line-dash-offset': 0
17089 }, [{
17090 name: 'arrow-shape',
17091 value: 'none'
17092 }, {
17093 name: 'arrow-color',
17094 value: '#999'
17095 }, {
17096 name: 'arrow-fill',
17097 value: 'filled'
17098 }].reduce(function (css, prop) {
17099 styfn$6.arrowPrefixes.forEach(function (prefix) {
17100 var name = prefix + '-' + prop.name;
17101 var val = prop.value;
17102 css[name] = val;
17103 });
17104 return css;
17105 }, {}));
17106 var parsedProps = {};
17107
17108 for (var i = 0; i < this.properties.length; i++) {
17109 var prop = this.properties[i];
17110
17111 if (prop.pointsTo) {
17112 continue;
17113 }
17114
17115 var name = prop.name;
17116 var val = rawProps[name];
17117 var parsedProp = this.parse(name, val);
17118 parsedProps[name] = parsedProp;
17119 }
17120
17121 _p.defaultProperties = parsedProps;
17122 return _p.defaultProperties;
17123};
17124
17125styfn$6.addDefaultStylesheet = function () {
17126 this.selector(':parent').css({
17127 'shape': 'rectangle',
17128 'padding': 10,
17129 'background-color': '#eee',
17130 'border-color': '#ccc',
17131 'border-width': 1
17132 }).selector('edge').css({
17133 'width': 3
17134 }).selector(':loop').css({
17135 'curve-style': 'bezier'
17136 }).selector('edge:compound').css({
17137 'curve-style': 'bezier',
17138 'source-endpoint': 'outside-to-line',
17139 'target-endpoint': 'outside-to-line'
17140 }).selector(':selected').css({
17141 'background-color': '#0169D9',
17142 'line-color': '#0169D9',
17143 'source-arrow-color': '#0169D9',
17144 'target-arrow-color': '#0169D9',
17145 'mid-source-arrow-color': '#0169D9',
17146 'mid-target-arrow-color': '#0169D9'
17147 }).selector(':parent:selected').css({
17148 'background-color': '#CCE1F9',
17149 'border-color': '#aec8e5'
17150 }).selector(':active').css({
17151 'overlay-color': 'black',
17152 'overlay-padding': 10,
17153 'overlay-opacity': 0.25
17154 });
17155 this.defaultLength = this.length;
17156};
17157
17158var styfn$7 = {}; // a caching layer for property parsing
17159
17160styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
17161 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17162
17163 if (fn(value)) {
17164 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17165 }
17166
17167 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17168 var bypassKey = propIsBypass ? 't' : 'f';
17169 var valueKey = '' + value;
17170 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17171 var propCache = self.propCache = self.propCache || [];
17172 var ret;
17173
17174 if (!(ret = propCache[argHash])) {
17175 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17176 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17177 // - mappings can't be shared b/c mappings are per-element
17178
17179
17180 if (propIsBypass || propIsFlat === 'mapping') {
17181 // need a copy since props are mutated later in their lifecycles
17182 ret = copy(ret);
17183
17184 if (ret) {
17185 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17186 }
17187 }
17188
17189 return ret;
17190};
17191
17192styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17193 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17194
17195 if (!prop && value != null) {
17196 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17197 }
17198
17199 return prop;
17200}; // parse a property; return null on invalid; return parsed property otherwise
17201// fields :
17202// - name : the name of the property
17203// - value : the parsed, native-typed value of the property
17204// - strValue : a string value that represents the property value in valid css
17205// - bypass : true iff the property is a bypass property
17206
17207
17208styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17209 var self = this;
17210 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17211
17212 var property = self.properties[name];
17213 var passedValue = value;
17214 var types = self.types;
17215
17216 if (!property) {
17217 return null;
17218 } // return null on property of unknown name
17219
17220
17221 if (value === undefined) {
17222 return null;
17223 } // can't assign undefined
17224 // the property may be an alias
17225
17226
17227 if (property.alias) {
17228 property = property.pointsTo;
17229 name = property.name;
17230 }
17231
17232 var valueIsString = string(value);
17233
17234 if (valueIsString) {
17235 // trim the value to make parsing easier
17236 value = value.trim();
17237 }
17238
17239 var type = property.type;
17240
17241 if (!type) {
17242 return null;
17243 } // no type, no luck
17244 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17245
17246
17247 if (propIsBypass && (value === '' || value === null)) {
17248 return {
17249 name: name,
17250 value: value,
17251 bypass: true,
17252 deleteBypass: true
17253 };
17254 } // check if value is a function used as a mapper
17255
17256
17257 if (fn(value)) {
17258 return {
17259 name: name,
17260 value: value,
17261 strValue: 'fn',
17262 mapped: types.fn,
17263 bypass: propIsBypass
17264 };
17265 } // check if value is mapped
17266
17267
17268 var data, mapData;
17269
17270 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))) {
17271 if (propIsBypass) {
17272 return false;
17273 } // mappers not allowed in bypass
17274
17275
17276 var mapped = types.data;
17277 return {
17278 name: name,
17279 value: data,
17280 strValue: '' + value,
17281 mapped: mapped,
17282 field: data[1],
17283 bypass: propIsBypass
17284 };
17285 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17286 if (propIsBypass) {
17287 return false;
17288 } // mappers not allowed in bypass
17289
17290
17291 if (type.multiple) {
17292 return false;
17293 } // impossible to map to num
17294
17295
17296 var _mapped = types.mapData; // we can map only if the type is a colour or a number
17297
17298 if (!(type.color || type.number)) {
17299 return false;
17300 }
17301
17302 var valueMin = this.parse(name, mapData[4]); // parse to validate
17303
17304 if (!valueMin || valueMin.mapped) {
17305 return false;
17306 } // can't be invalid or mapped
17307
17308
17309 var valueMax = this.parse(name, mapData[5]); // parse to validate
17310
17311 if (!valueMax || valueMax.mapped) {
17312 return false;
17313 } // can't be invalid or mapped
17314 // check if valueMin and valueMax are the same
17315
17316
17317 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17318 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17319 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17320 } else if (type.color) {
17321 var c1 = valueMin.value;
17322 var c2 = valueMax.value;
17323 var same = c1[0] === c2[0] // red
17324 && c1[1] === c2[1] // green
17325 && c1[2] === c2[2] // blue
17326 && ( // optional alpha
17327 c1[3] === c2[3] // same alpha outright
17328 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
17329 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17330 );
17331
17332 if (same) {
17333 return false;
17334 } // can't make a mapper without a range
17335
17336 }
17337
17338 return {
17339 name: name,
17340 value: mapData,
17341 strValue: '' + value,
17342 mapped: _mapped,
17343 field: mapData[1],
17344 fieldMin: parseFloat(mapData[2]),
17345 // min & max are numeric
17346 fieldMax: parseFloat(mapData[3]),
17347 valueMin: valueMin.value,
17348 valueMax: valueMax.value,
17349 bypass: propIsBypass
17350 };
17351 }
17352
17353 if (type.multiple && propIsFlat !== 'multiple') {
17354 var vals;
17355
17356 if (valueIsString) {
17357 vals = value.split(/\s+/);
17358 } else if (array(value)) {
17359 vals = value;
17360 } else {
17361 vals = [value];
17362 }
17363
17364 if (type.evenMultiple && vals.length % 2 !== 0) {
17365 return null;
17366 }
17367
17368 var valArr = [];
17369 var unitsArr = [];
17370 var pfValArr = [];
17371 var strVal = '';
17372 var hasEnum = false;
17373
17374 for (var i = 0; i < vals.length; i++) {
17375 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17376 hasEnum = hasEnum || string(p.value);
17377 valArr.push(p.value);
17378 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17379 unitsArr.push(p.units);
17380 strVal += (i > 0 ? ' ' : '') + p.strValue;
17381 }
17382
17383 if (type.validate && !type.validate(valArr, unitsArr)) {
17384 return null;
17385 }
17386
17387 if (type.singleEnum && hasEnum) {
17388 if (valArr.length === 1 && string(valArr[0])) {
17389 return {
17390 name: name,
17391 value: valArr[0],
17392 strValue: valArr[0],
17393 bypass: propIsBypass
17394 };
17395 } else {
17396 return null;
17397 }
17398 }
17399
17400 return {
17401 name: name,
17402 value: valArr,
17403 pfValue: pfValArr,
17404 strValue: strVal,
17405 bypass: propIsBypass,
17406 units: unitsArr
17407 };
17408 } // several types also allow enums
17409
17410
17411 var checkEnums = function checkEnums() {
17412 for (var _i = 0; _i < type.enums.length; _i++) {
17413 var en = type.enums[_i];
17414
17415 if (en === value) {
17416 return {
17417 name: name,
17418 value: value,
17419 strValue: '' + value,
17420 bypass: propIsBypass
17421 };
17422 }
17423 }
17424
17425 return null;
17426 }; // check the type and return the appropriate object
17427
17428
17429 if (type.number) {
17430 var units;
17431 var implicitUnits = 'px'; // not set => px
17432
17433 if (type.units) {
17434 // use specified units if set
17435 units = type.units;
17436 }
17437
17438 if (type.implicitUnits) {
17439 implicitUnits = type.implicitUnits;
17440 }
17441
17442 if (!type.unitless) {
17443 if (valueIsString) {
17444 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17445
17446 if (units) {
17447 unitsRegex = units;
17448 } // only allow explicit units if so set
17449
17450
17451 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
17452
17453 if (match) {
17454 value = match[1];
17455 units = match[2] || implicitUnits;
17456 }
17457 } else if (!units || type.implicitUnits) {
17458 units = implicitUnits; // implicitly px if unspecified
17459 }
17460 }
17461
17462 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
17463
17464 if (isNaN(value) && type.enums === undefined) {
17465 return null;
17466 } // check if this number type also accepts special keywords in place of numbers
17467 // (i.e. `left`, `auto`, etc)
17468
17469
17470 if (isNaN(value) && type.enums !== undefined) {
17471 value = passedValue;
17472 return checkEnums();
17473 } // check if value must be an integer
17474
17475
17476 if (type.integer && !integer(value)) {
17477 return null;
17478 } // check value is within range
17479
17480
17481 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17482 return null;
17483 }
17484
17485 var ret = {
17486 name: name,
17487 value: value,
17488 strValue: '' + value + (units ? units : ''),
17489 units: units,
17490 bypass: propIsBypass
17491 }; // normalise value in pixels
17492
17493 if (type.unitless || units !== 'px' && units !== 'em') {
17494 ret.pfValue = value;
17495 } else {
17496 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17497 } // normalise value in ms
17498
17499
17500 if (units === 'ms' || units === 's') {
17501 ret.pfValue = units === 'ms' ? value : 1000 * value;
17502 } // normalise value in rad
17503
17504
17505 if (units === 'deg' || units === 'rad') {
17506 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17507 } // normalize value in %
17508
17509
17510 if (units === '%') {
17511 ret.pfValue = value / 100;
17512 }
17513
17514 return ret;
17515 } else if (type.propList) {
17516 var props = [];
17517 var propsStr = '' + value;
17518
17519 if (propsStr === 'none') ; else {
17520 // go over each prop
17521 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17522
17523 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17524 var propName = propsSplit[_i2].trim();
17525
17526 if (self.properties[propName]) {
17527 props.push(propName);
17528 } else {
17529 warn('`' + propName + '` is not a valid property name');
17530 }
17531 }
17532
17533 if (props.length === 0) {
17534 return null;
17535 }
17536 }
17537
17538 return {
17539 name: name,
17540 value: props,
17541 strValue: props.length === 0 ? 'none' : props.join(' '),
17542 bypass: propIsBypass
17543 };
17544 } else if (type.color) {
17545 var tuple = color2tuple(value);
17546
17547 if (!tuple) {
17548 return null;
17549 }
17550
17551 return {
17552 name: name,
17553 value: tuple,
17554 pfValue: tuple,
17555 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
17556 // n.b. no spaces b/c of multiple support
17557 bypass: propIsBypass
17558 };
17559 } else if (type.regex || type.regexes) {
17560 // first check enums
17561 if (type.enums) {
17562 var enumProp = checkEnums();
17563
17564 if (enumProp) {
17565 return enumProp;
17566 }
17567 }
17568
17569 var regexes = type.regexes ? type.regexes : [type.regex];
17570
17571 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
17572 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
17573
17574 var m = regex.exec(value);
17575
17576 if (m) {
17577 // regex matches
17578 return {
17579 name: name,
17580 value: type.singleRegexMatchValue ? m[1] : m,
17581 strValue: '' + value,
17582 bypass: propIsBypass
17583 };
17584 }
17585 }
17586
17587 return null; // didn't match any
17588 } else if (type.string) {
17589 // just return
17590 return {
17591 name: name,
17592 value: '' + value,
17593 strValue: '' + value,
17594 bypass: propIsBypass
17595 };
17596 } else if (type.enums) {
17597 // check enums last because it's a combo type in others
17598 return checkEnums();
17599 } else {
17600 return null; // not a type we can handle
17601 }
17602};
17603
17604var Style = function Style(cy) {
17605 if (!(this instanceof Style)) {
17606 return new Style(cy);
17607 }
17608
17609 if (!core(cy)) {
17610 error('A style must have a core reference');
17611 return;
17612 }
17613
17614 this._private = {
17615 cy: cy,
17616 coreStyle: {}
17617 };
17618 this.length = 0;
17619 this.resetToDefault();
17620};
17621
17622var styfn$8 = Style.prototype;
17623
17624styfn$8.instanceString = function () {
17625 return 'style';
17626}; // remove all contexts
17627
17628
17629styfn$8.clear = function () {
17630 for (var i = 0; i < this.length; i++) {
17631 this[i] = undefined;
17632 }
17633
17634 this.length = 0;
17635 var _p = this._private;
17636 _p.newStyle = true;
17637 return this; // chaining
17638};
17639
17640styfn$8.resetToDefault = function () {
17641 this.clear();
17642 this.addDefaultStylesheet();
17643 return this;
17644}; // builds a style object for the 'core' selector
17645
17646
17647styfn$8.core = function (propName) {
17648 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
17649}; // create a new context from the specified selector string and switch to that context
17650
17651
17652styfn$8.selector = function (selectorStr) {
17653 // 'core' is a special case and does not need a selector
17654 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
17655 var i = this.length++; // new context means new index
17656
17657 this[i] = {
17658 selector: selector,
17659 properties: [],
17660 mappedProperties: [],
17661 index: i
17662 };
17663 return this; // chaining
17664}; // add one or many css rules to the current context
17665
17666
17667styfn$8.css = function () {
17668 var self = this;
17669 var args = arguments;
17670
17671 if (args.length === 1) {
17672 var map = args[0];
17673
17674 for (var i = 0; i < self.properties.length; i++) {
17675 var prop = self.properties[i];
17676 var mapVal = map[prop.name];
17677
17678 if (mapVal === undefined) {
17679 mapVal = map[dash2camel(prop.name)];
17680 }
17681
17682 if (mapVal !== undefined) {
17683 this.cssRule(prop.name, mapVal);
17684 }
17685 }
17686 } else if (args.length === 2) {
17687 this.cssRule(args[0], args[1]);
17688 } // do nothing if args are invalid
17689
17690
17691 return this; // chaining
17692};
17693
17694styfn$8.style = styfn$8.css; // add a single css rule to the current context
17695
17696styfn$8.cssRule = function (name, value) {
17697 // name-value pair
17698 var property = this.parse(name, value); // add property to current context if valid
17699
17700 if (property) {
17701 var i = this.length - 1;
17702 this[i].properties.push(property);
17703 this[i].properties[property.name] = property; // allow access by name as well
17704
17705 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
17706 this._private.hasPie = true;
17707 }
17708
17709 if (property.mapped) {
17710 this[i].mappedProperties.push(property);
17711 } // add to core style if necessary
17712
17713
17714 var currentSelectorIsCore = !this[i].selector;
17715
17716 if (currentSelectorIsCore) {
17717 this._private.coreStyle[property.name] = property;
17718 }
17719 }
17720
17721 return this; // chaining
17722};
17723
17724styfn$8.append = function (style) {
17725 if (stylesheet(style)) {
17726 style.appendToStyle(this);
17727 } else if (array(style)) {
17728 this.appendFromJson(style);
17729 } else if (string(style)) {
17730 this.appendFromString(style);
17731 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
17732
17733
17734 return this;
17735}; // static function
17736
17737
17738Style.fromJson = function (cy, json) {
17739 var style = new Style(cy);
17740 style.fromJson(json);
17741 return style;
17742};
17743
17744Style.fromString = function (cy, string) {
17745 return new Style(cy).fromString(string);
17746};
17747
17748[styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
17749 extend(styfn$8, props);
17750});
17751Style.types = styfn$8.types;
17752Style.properties = styfn$8.properties;
17753Style.propertyGroups = styfn$8.propertyGroups;
17754Style.propertyGroupNames = styfn$8.propertyGroupNames;
17755Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
17756
17757var corefn$7 = {
17758 style: function style(newStyle) {
17759 if (newStyle) {
17760 var s = this.setStyle(newStyle);
17761 s.update();
17762 }
17763
17764 return this._private.style;
17765 },
17766 setStyle: function setStyle(style) {
17767 var _p = this._private;
17768
17769 if (stylesheet(style)) {
17770 _p.style = style.generateStyle(this);
17771 } else if (array(style)) {
17772 _p.style = Style.fromJson(this, style);
17773 } else if (string(style)) {
17774 _p.style = Style.fromString(this, style);
17775 } else {
17776 _p.style = Style(this);
17777 }
17778
17779 return _p.style;
17780 }
17781};
17782
17783var defaultSelectionType = 'single';
17784var corefn$8 = {
17785 autolock: function autolock(bool) {
17786 if (bool !== undefined) {
17787 this._private.autolock = bool ? true : false;
17788 } else {
17789 return this._private.autolock;
17790 }
17791
17792 return this; // chaining
17793 },
17794 autoungrabify: function autoungrabify(bool) {
17795 if (bool !== undefined) {
17796 this._private.autoungrabify = bool ? true : false;
17797 } else {
17798 return this._private.autoungrabify;
17799 }
17800
17801 return this; // chaining
17802 },
17803 autounselectify: function autounselectify(bool) {
17804 if (bool !== undefined) {
17805 this._private.autounselectify = bool ? true : false;
17806 } else {
17807 return this._private.autounselectify;
17808 }
17809
17810 return this; // chaining
17811 },
17812 selectionType: function selectionType(selType) {
17813 var _p = this._private;
17814
17815 if (_p.selectionType == null) {
17816 _p.selectionType = defaultSelectionType;
17817 }
17818
17819 if (selType !== undefined) {
17820 if (selType === 'additive' || selType === 'single') {
17821 _p.selectionType = selType;
17822 }
17823 } else {
17824 return _p.selectionType;
17825 }
17826
17827 return this;
17828 },
17829 panningEnabled: function panningEnabled(bool) {
17830 if (bool !== undefined) {
17831 this._private.panningEnabled = bool ? true : false;
17832 } else {
17833 return this._private.panningEnabled;
17834 }
17835
17836 return this; // chaining
17837 },
17838 userPanningEnabled: function userPanningEnabled(bool) {
17839 if (bool !== undefined) {
17840 this._private.userPanningEnabled = bool ? true : false;
17841 } else {
17842 return this._private.userPanningEnabled;
17843 }
17844
17845 return this; // chaining
17846 },
17847 zoomingEnabled: function zoomingEnabled(bool) {
17848 if (bool !== undefined) {
17849 this._private.zoomingEnabled = bool ? true : false;
17850 } else {
17851 return this._private.zoomingEnabled;
17852 }
17853
17854 return this; // chaining
17855 },
17856 userZoomingEnabled: function userZoomingEnabled(bool) {
17857 if (bool !== undefined) {
17858 this._private.userZoomingEnabled = bool ? true : false;
17859 } else {
17860 return this._private.userZoomingEnabled;
17861 }
17862
17863 return this; // chaining
17864 },
17865 boxSelectionEnabled: function boxSelectionEnabled(bool) {
17866 if (bool !== undefined) {
17867 this._private.boxSelectionEnabled = bool ? true : false;
17868 } else {
17869 return this._private.boxSelectionEnabled;
17870 }
17871
17872 return this; // chaining
17873 },
17874 pan: function pan() {
17875 var args = arguments;
17876 var pan = this._private.pan;
17877 var dim, val, dims, x, y;
17878
17879 switch (args.length) {
17880 case 0:
17881 // .pan()
17882 return pan;
17883
17884 case 1:
17885 if (string(args[0])) {
17886 // .pan('x')
17887 dim = args[0];
17888 return pan[dim];
17889 } else if (plainObject(args[0])) {
17890 // .pan({ x: 0, y: 100 })
17891 if (!this._private.panningEnabled) {
17892 return this;
17893 }
17894
17895 dims = args[0];
17896 x = dims.x;
17897 y = dims.y;
17898
17899 if (number(x)) {
17900 pan.x = x;
17901 }
17902
17903 if (number(y)) {
17904 pan.y = y;
17905 }
17906
17907 this.emit('pan viewport');
17908 }
17909
17910 break;
17911
17912 case 2:
17913 // .pan('x', 100)
17914 if (!this._private.panningEnabled) {
17915 return this;
17916 }
17917
17918 dim = args[0];
17919 val = args[1];
17920
17921 if ((dim === 'x' || dim === 'y') && number(val)) {
17922 pan[dim] = val;
17923 }
17924
17925 this.emit('pan viewport');
17926 break;
17927 // invalid
17928 }
17929
17930 this.notify('viewport');
17931 return this; // chaining
17932 },
17933 panBy: function panBy(arg0, arg1) {
17934 var args = arguments;
17935 var pan = this._private.pan;
17936 var dim, val, dims, x, y;
17937
17938 if (!this._private.panningEnabled) {
17939 return this;
17940 }
17941
17942 switch (args.length) {
17943 case 1:
17944 if (plainObject(arg0)) {
17945 // .panBy({ x: 0, y: 100 })
17946 dims = args[0];
17947 x = dims.x;
17948 y = dims.y;
17949
17950 if (number(x)) {
17951 pan.x += x;
17952 }
17953
17954 if (number(y)) {
17955 pan.y += y;
17956 }
17957
17958 this.emit('pan viewport');
17959 }
17960
17961 break;
17962
17963 case 2:
17964 // .panBy('x', 100)
17965 dim = arg0;
17966 val = arg1;
17967
17968 if ((dim === 'x' || dim === 'y') && number(val)) {
17969 pan[dim] += val;
17970 }
17971
17972 this.emit('pan viewport');
17973 break;
17974 // invalid
17975 }
17976
17977 this.notify('viewport');
17978 return this; // chaining
17979 },
17980 fit: function fit(elements, padding) {
17981 var viewportState = this.getFitViewport(elements, padding);
17982
17983 if (viewportState) {
17984 var _p = this._private;
17985 _p.zoom = viewportState.zoom;
17986 _p.pan = viewportState.pan;
17987 this.emit('pan zoom viewport');
17988 this.notify('viewport');
17989 }
17990
17991 return this; // chaining
17992 },
17993 getFitViewport: function getFitViewport(elements, padding) {
17994 if (number(elements) && padding === undefined) {
17995 // elements is optional
17996 padding = elements;
17997 elements = undefined;
17998 }
17999
18000 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18001 return;
18002 }
18003
18004 var bb;
18005
18006 if (string(elements)) {
18007 var sel = elements;
18008 elements = this.$(sel);
18009 } else if (boundingBox(elements)) {
18010 // assume bb
18011 var bbe = elements;
18012 bb = {
18013 x1: bbe.x1,
18014 y1: bbe.y1,
18015 x2: bbe.x2,
18016 y2: bbe.y2
18017 };
18018 bb.w = bb.x2 - bb.x1;
18019 bb.h = bb.y2 - bb.y1;
18020 } else if (!elementOrCollection(elements)) {
18021 elements = this.mutableElements();
18022 }
18023
18024 if (elementOrCollection(elements) && elements.empty()) {
18025 return;
18026 } // can't fit to nothing
18027
18028
18029 bb = bb || elements.boundingBox();
18030 var w = this.width();
18031 var h = this.height();
18032 var zoom;
18033 padding = number(padding) ? padding : 0;
18034
18035 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18036 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18037
18038 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18039 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18040 var pan = {
18041 // now pan to middle
18042 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18043 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18044 };
18045 return {
18046 zoom: zoom,
18047 pan: pan
18048 };
18049 }
18050
18051 return;
18052 },
18053 zoomRange: function zoomRange(min, max) {
18054 var _p = this._private;
18055
18056 if (max == null) {
18057 var opts = min;
18058 min = opts.min;
18059 max = opts.max;
18060 }
18061
18062 if (number(min) && number(max) && min <= max) {
18063 _p.minZoom = min;
18064 _p.maxZoom = max;
18065 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
18066 _p.minZoom = min;
18067 } else if (number(max) && min === undefined && max >= _p.minZoom) {
18068 _p.maxZoom = max;
18069 }
18070
18071 return this;
18072 },
18073 minZoom: function minZoom(zoom) {
18074 if (zoom === undefined) {
18075 return this._private.minZoom;
18076 } else {
18077 return this.zoomRange({
18078 min: zoom
18079 });
18080 }
18081 },
18082 maxZoom: function maxZoom(zoom) {
18083 if (zoom === undefined) {
18084 return this._private.maxZoom;
18085 } else {
18086 return this.zoomRange({
18087 max: zoom
18088 });
18089 }
18090 },
18091 getZoomedViewport: function getZoomedViewport(params) {
18092 var _p = this._private;
18093 var currentPan = _p.pan;
18094 var currentZoom = _p.zoom;
18095 var pos; // in rendered px
18096
18097 var zoom;
18098 var bail = false;
18099
18100 if (!_p.zoomingEnabled) {
18101 // zooming disabled
18102 bail = true;
18103 }
18104
18105 if (number(params)) {
18106 // then set the zoom
18107 zoom = params;
18108 } else if (plainObject(params)) {
18109 // then zoom about a point
18110 zoom = params.level;
18111
18112 if (params.position != null) {
18113 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18114 } else if (params.renderedPosition != null) {
18115 pos = params.renderedPosition;
18116 }
18117
18118 if (pos != null && !_p.panningEnabled) {
18119 // panning disabled
18120 bail = true;
18121 }
18122 } // crop zoom
18123
18124
18125 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18126 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18127
18128 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
18129 return null;
18130 }
18131
18132 if (pos != null) {
18133 // set zoom about position
18134 var pan1 = currentPan;
18135 var zoom1 = currentZoom;
18136 var zoom2 = zoom;
18137 var pan2 = {
18138 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18139 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18140 };
18141 return {
18142 zoomed: true,
18143 panned: true,
18144 zoom: zoom2,
18145 pan: pan2
18146 };
18147 } else {
18148 // just set the zoom
18149 return {
18150 zoomed: true,
18151 panned: false,
18152 zoom: zoom,
18153 pan: currentPan
18154 };
18155 }
18156 },
18157 zoom: function zoom(params) {
18158 if (params === undefined) {
18159 // get
18160 return this._private.zoom;
18161 } else {
18162 // set
18163 var vp = this.getZoomedViewport(params);
18164 var _p = this._private;
18165
18166 if (vp == null || !vp.zoomed) {
18167 return this;
18168 }
18169
18170 _p.zoom = vp.zoom;
18171
18172 if (vp.panned) {
18173 _p.pan.x = vp.pan.x;
18174 _p.pan.y = vp.pan.y;
18175 }
18176
18177 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18178 this.notify('viewport');
18179 return this; // chaining
18180 }
18181 },
18182 viewport: function viewport(opts) {
18183 var _p = this._private;
18184 var zoomDefd = true;
18185 var panDefd = true;
18186 var events = []; // to trigger
18187
18188 var zoomFailed = false;
18189 var panFailed = false;
18190
18191 if (!opts) {
18192 return this;
18193 }
18194
18195 if (!number(opts.zoom)) {
18196 zoomDefd = false;
18197 }
18198
18199 if (!plainObject(opts.pan)) {
18200 panDefd = false;
18201 }
18202
18203 if (!zoomDefd && !panDefd) {
18204 return this;
18205 }
18206
18207 if (zoomDefd) {
18208 var z = opts.zoom;
18209
18210 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18211 zoomFailed = true;
18212 } else {
18213 _p.zoom = z;
18214 events.push('zoom');
18215 }
18216 }
18217
18218 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18219 var p = opts.pan;
18220
18221 if (number(p.x)) {
18222 _p.pan.x = p.x;
18223 panFailed = false;
18224 }
18225
18226 if (number(p.y)) {
18227 _p.pan.y = p.y;
18228 panFailed = false;
18229 }
18230
18231 if (!panFailed) {
18232 events.push('pan');
18233 }
18234 }
18235
18236 if (events.length > 0) {
18237 events.push('viewport');
18238 this.emit(events.join(' '));
18239 this.notify('viewport');
18240 }
18241
18242 return this; // chaining
18243 },
18244 center: function center(elements) {
18245 var pan = this.getCenterPan(elements);
18246
18247 if (pan) {
18248 this._private.pan = pan;
18249 this.emit('pan viewport');
18250 this.notify('viewport');
18251 }
18252
18253 return this; // chaining
18254 },
18255 getCenterPan: function getCenterPan(elements, zoom) {
18256 if (!this._private.panningEnabled) {
18257 return;
18258 }
18259
18260 if (string(elements)) {
18261 var selector = elements;
18262 elements = this.mutableElements().filter(selector);
18263 } else if (!elementOrCollection(elements)) {
18264 elements = this.mutableElements();
18265 }
18266
18267 if (elements.length === 0) {
18268 return;
18269 } // can't centre pan to nothing
18270
18271
18272 var bb = elements.boundingBox();
18273 var w = this.width();
18274 var h = this.height();
18275 zoom = zoom === undefined ? this._private.zoom : zoom;
18276 var pan = {
18277 // middle
18278 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18279 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18280 };
18281 return pan;
18282 },
18283 reset: function reset() {
18284 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18285 return this;
18286 }
18287
18288 this.viewport({
18289 pan: {
18290 x: 0,
18291 y: 0
18292 },
18293 zoom: 1
18294 });
18295 return this; // chaining
18296 },
18297 invalidateSize: function invalidateSize() {
18298 this._private.sizeCache = null;
18299 },
18300 size: function size() {
18301 var _p = this._private;
18302 var container = _p.container;
18303 return _p.sizeCache = _p.sizeCache || (container ? function () {
18304 var style = window$1.getComputedStyle(container);
18305
18306 var val = function val(name) {
18307 return parseFloat(style.getPropertyValue(name));
18308 };
18309
18310 return {
18311 width: container.clientWidth - val('padding-left') - val('padding-right'),
18312 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18313 };
18314 }() : {
18315 // fallback if no container (not 0 b/c can be used for dividing etc)
18316 width: 1,
18317 height: 1
18318 });
18319 },
18320 width: function width() {
18321 return this.size().width;
18322 },
18323 height: function height() {
18324 return this.size().height;
18325 },
18326 extent: function extent() {
18327 var pan = this._private.pan;
18328 var zoom = this._private.zoom;
18329 var rb = this.renderedExtent();
18330 var b = {
18331 x1: (rb.x1 - pan.x) / zoom,
18332 x2: (rb.x2 - pan.x) / zoom,
18333 y1: (rb.y1 - pan.y) / zoom,
18334 y2: (rb.y2 - pan.y) / zoom
18335 };
18336 b.w = b.x2 - b.x1;
18337 b.h = b.y2 - b.y1;
18338 return b;
18339 },
18340 renderedExtent: function renderedExtent() {
18341 var width = this.width();
18342 var height = this.height();
18343 return {
18344 x1: 0,
18345 y1: 0,
18346 x2: width,
18347 y2: height,
18348 w: width,
18349 h: height
18350 };
18351 }
18352}; // aliases
18353
18354corefn$8.centre = corefn$8.center; // backwards compatibility
18355
18356corefn$8.autolockNodes = corefn$8.autolock;
18357corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
18358
18359var fn$6 = {
18360 data: define$3.data({
18361 field: 'data',
18362 bindingEvent: 'data',
18363 allowBinding: true,
18364 allowSetting: true,
18365 settingEvent: 'data',
18366 settingTriggersEvent: true,
18367 triggerFnName: 'trigger',
18368 allowGetting: true
18369 }),
18370 removeData: define$3.removeData({
18371 field: 'data',
18372 event: 'data',
18373 triggerFnName: 'trigger',
18374 triggerEvent: true
18375 }),
18376 scratch: define$3.data({
18377 field: 'scratch',
18378 bindingEvent: 'scratch',
18379 allowBinding: true,
18380 allowSetting: true,
18381 settingEvent: 'scratch',
18382 settingTriggersEvent: true,
18383 triggerFnName: 'trigger',
18384 allowGetting: true
18385 }),
18386 removeScratch: define$3.removeData({
18387 field: 'scratch',
18388 event: 'scratch',
18389 triggerFnName: 'trigger',
18390 triggerEvent: true
18391 })
18392}; // aliases
18393
18394fn$6.attr = fn$6.data;
18395fn$6.removeAttr = fn$6.removeData;
18396
18397var Core = function Core(opts) {
18398 var cy = this;
18399 opts = extend({}, opts);
18400 var container = opts.container; // allow for passing a wrapped jquery object
18401 // e.g. cytoscape({ container: $('#cy') })
18402
18403 if (container && !htmlElement(container) && htmlElement(container[0])) {
18404 container = container[0];
18405 }
18406
18407 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18408
18409 reg = reg || {};
18410
18411 if (reg && reg.cy) {
18412 reg.cy.destroy();
18413 reg = {}; // old instance => replace reg completely
18414 }
18415
18416 var readies = reg.readies = reg.readies || [];
18417
18418 if (container) {
18419 container._cyreg = reg;
18420 } // make sure container assoc'd reg points to this cy
18421
18422
18423 reg.cy = cy;
18424 var head = window$1 !== undefined && container !== undefined && !opts.headless;
18425 var options = opts;
18426 options.layout = extend({
18427 name: head ? 'grid' : 'null'
18428 }, options.layout);
18429 options.renderer = extend({
18430 name: head ? 'canvas' : 'null'
18431 }, options.renderer);
18432
18433 var defVal = function defVal(def, val, altVal) {
18434 if (val !== undefined) {
18435 return val;
18436 } else if (altVal !== undefined) {
18437 return altVal;
18438 } else {
18439 return def;
18440 }
18441 };
18442
18443 var _p = this._private = {
18444 container: container,
18445 // html dom ele container
18446 ready: false,
18447 // whether ready has been triggered
18448 options: options,
18449 // cached options
18450 elements: new Collection(this),
18451 // elements in the graph
18452 listeners: [],
18453 // list of listeners
18454 aniEles: new Collection(this),
18455 // elements being animated
18456 data: {},
18457 // data for the core
18458 scratch: {},
18459 // scratch object for core
18460 layout: null,
18461 renderer: null,
18462 destroyed: false,
18463 // whether destroy was called
18464 notificationsEnabled: true,
18465 // whether notifications are sent to the renderer
18466 minZoom: 1e-50,
18467 maxZoom: 1e50,
18468 zoomingEnabled: defVal(true, options.zoomingEnabled),
18469 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18470 panningEnabled: defVal(true, options.panningEnabled),
18471 userPanningEnabled: defVal(true, options.userPanningEnabled),
18472 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18473 autolock: defVal(false, options.autolock, options.autolockNodes),
18474 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18475 autounselectify: defVal(false, options.autounselectify),
18476 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18477 zoom: number(options.zoom) ? options.zoom : 1,
18478 pan: {
18479 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
18480 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
18481 },
18482 animation: {
18483 // object for currently-running animations
18484 current: [],
18485 queue: []
18486 },
18487 hasCompoundNodes: false
18488 };
18489
18490 this.createEmitter(); // set selection type
18491
18492 this.selectionType(options.selectionType); // init zoom bounds
18493
18494 this.zoomRange({
18495 min: options.minZoom,
18496 max: options.maxZoom
18497 });
18498
18499 var loadExtData = function loadExtData(extData, next) {
18500 var anyIsPromise = extData.some(promise);
18501
18502 if (anyIsPromise) {
18503 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18504 } else {
18505 next(extData); // exec synchronously for convenience
18506 }
18507 }; // start with the default stylesheet so we have something before loading an external stylesheet
18508
18509
18510 if (_p.styleEnabled) {
18511 cy.setStyle([]);
18512 } // create the renderer
18513
18514
18515 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18516
18517 cy.initRenderer(rendererOptions);
18518
18519 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18520 cy.notifications(false); // remove old elements
18521
18522 var oldEles = cy.mutableElements();
18523
18524 if (oldEles.length > 0) {
18525 oldEles.remove();
18526 }
18527
18528 if (elements != null) {
18529 if (plainObject(elements) || array(elements)) {
18530 cy.add(elements);
18531 }
18532 }
18533
18534 cy.one('layoutready', function (e) {
18535 cy.notifications(true);
18536 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18537
18538 cy.one('load', onload);
18539 cy.emitAndNotify('load');
18540 }).one('layoutstop', function () {
18541 cy.one('done', ondone);
18542 cy.emit('done');
18543 });
18544 var layoutOpts = extend({}, cy._private.options.layout);
18545 layoutOpts.eles = cy.elements();
18546 cy.layout(layoutOpts).run();
18547 };
18548
18549 loadExtData([options.style, options.elements], function (thens) {
18550 var initStyle = thens[0];
18551 var initEles = thens[1]; // init style
18552
18553 if (_p.styleEnabled) {
18554 cy.style().append(initStyle);
18555 } // initial load
18556
18557
18558 setElesAndLayout(initEles, function () {
18559 // onready
18560 cy.startAnimationLoop();
18561 _p.ready = true; // if a ready callback is specified as an option, the bind it
18562
18563 if (fn(options.ready)) {
18564 cy.on('ready', options.ready);
18565 } // bind all the ready handlers registered before creating this instance
18566
18567
18568 for (var i = 0; i < readies.length; i++) {
18569 var fn$1 = readies[i];
18570 cy.on('ready', fn$1);
18571 }
18572
18573 if (reg) {
18574 reg.readies = [];
18575 } // 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
18576
18577
18578 cy.emit('ready');
18579 }, options.done);
18580 });
18581};
18582
18583var corefn$9 = Core.prototype; // short alias
18584
18585extend(corefn$9, {
18586 instanceString: function instanceString() {
18587 return 'core';
18588 },
18589 isReady: function isReady() {
18590 return this._private.ready;
18591 },
18592 destroyed: function destroyed() {
18593 return this._private.destroyed;
18594 },
18595 ready: function ready(fn) {
18596 if (this.isReady()) {
18597 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18598 } else {
18599 this.on('ready', fn);
18600 }
18601
18602 return this;
18603 },
18604 destroy: function destroy() {
18605 var cy = this;
18606 if (cy.destroyed()) return;
18607 cy.stopAnimationLoop();
18608 cy.destroyRenderer();
18609 this.emit('destroy');
18610 cy._private.destroyed = true;
18611 return cy;
18612 },
18613 hasElementWithId: function hasElementWithId(id) {
18614 return this._private.elements.hasElementWithId(id);
18615 },
18616 getElementById: function getElementById(id) {
18617 return this._private.elements.getElementById(id);
18618 },
18619 hasCompoundNodes: function hasCompoundNodes() {
18620 return this._private.hasCompoundNodes;
18621 },
18622 headless: function headless() {
18623 return this._private.renderer.isHeadless();
18624 },
18625 styleEnabled: function styleEnabled() {
18626 return this._private.styleEnabled;
18627 },
18628 addToPool: function addToPool(eles) {
18629 this._private.elements.merge(eles);
18630
18631 return this; // chaining
18632 },
18633 removeFromPool: function removeFromPool(eles) {
18634 this._private.elements.unmerge(eles);
18635
18636 return this;
18637 },
18638 container: function container() {
18639 return this._private.container || null;
18640 },
18641 mount: function mount(container) {
18642 if (container == null) {
18643 return;
18644 }
18645
18646 var cy = this;
18647 var _p = cy._private;
18648 var options = _p.options;
18649
18650 if (!htmlElement(container) && htmlElement(container[0])) {
18651 container = container[0];
18652 }
18653
18654 cy.stopAnimationLoop();
18655 cy.destroyRenderer();
18656 _p.container = container;
18657 _p.styleEnabled = true;
18658 cy.invalidateSize();
18659 cy.initRenderer(extend({}, options, options.renderer, {
18660 // allow custom renderer name to be re-used, otherwise use canvas
18661 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
18662 }));
18663 cy.startAnimationLoop();
18664 cy.style(options.style);
18665 cy.emit('mount');
18666 return cy;
18667 },
18668 unmount: function unmount() {
18669 var cy = this;
18670 cy.stopAnimationLoop();
18671 cy.destroyRenderer();
18672 cy.initRenderer({
18673 name: 'null'
18674 });
18675 cy.emit('unmount');
18676 return cy;
18677 },
18678 options: function options() {
18679 return copy(this._private.options);
18680 },
18681 json: function json(obj) {
18682 var cy = this;
18683 var _p = cy._private;
18684 var eles = cy.mutableElements();
18685
18686 var getFreshRef = function getFreshRef(ele) {
18687 return cy.getElementById(ele.id());
18688 };
18689
18690 if (plainObject(obj)) {
18691 // set
18692 cy.startBatch();
18693
18694 if (obj.elements) {
18695 var idInJson = {};
18696
18697 var updateEles = function updateEles(jsons, gr) {
18698 var toAdd = [];
18699 var toMod = [];
18700
18701 for (var i = 0; i < jsons.length; i++) {
18702 var json = jsons[i];
18703 var id = '' + json.data.id; // id must be string
18704
18705 var ele = cy.getElementById(id);
18706 idInJson[id] = true;
18707
18708 if (ele.length !== 0) {
18709 // existing element should be updated
18710 toMod.push({
18711 ele: ele,
18712 json: json
18713 });
18714 } else {
18715 // otherwise should be added
18716 if (gr) {
18717 json.group = gr;
18718 toAdd.push(json);
18719 } else {
18720 toAdd.push(json);
18721 }
18722 }
18723 }
18724
18725 cy.add(toAdd);
18726
18727 for (var _i = 0; _i < toMod.length; _i++) {
18728 var _toMod$_i = toMod[_i],
18729 _ele = _toMod$_i.ele,
18730 _json = _toMod$_i.json;
18731
18732 _ele.json(_json);
18733 }
18734 };
18735
18736 if (array(obj.elements)) {
18737 // elements: []
18738 updateEles(obj.elements);
18739 } else {
18740 // elements: { nodes: [], edges: [] }
18741 var grs = ['nodes', 'edges'];
18742
18743 for (var i = 0; i < grs.length; i++) {
18744 var gr = grs[i];
18745 var elements = obj.elements[gr];
18746
18747 if (array(elements)) {
18748 updateEles(elements, gr);
18749 }
18750 }
18751 }
18752
18753 var parentsToRemove = cy.collection();
18754 eles.filter(function (ele) {
18755 return !idInJson[ele.id()];
18756 }).forEach(function (ele) {
18757 if (ele.isParent()) {
18758 parentsToRemove.merge(ele);
18759 } else {
18760 ele.remove();
18761 }
18762 }); // so that children are not removed w/parent
18763
18764 parentsToRemove.forEach(function (ele) {
18765 return ele.children().move({
18766 parent: null
18767 });
18768 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
18769
18770 parentsToRemove.forEach(function (ele) {
18771 return getFreshRef(ele).remove();
18772 });
18773 }
18774
18775 if (obj.style) {
18776 cy.style(obj.style);
18777 }
18778
18779 if (obj.zoom != null && obj.zoom !== _p.zoom) {
18780 cy.zoom(obj.zoom);
18781 }
18782
18783 if (obj.pan) {
18784 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
18785 cy.pan(obj.pan);
18786 }
18787 }
18788
18789 if (obj.data) {
18790 cy.data(obj.data);
18791 }
18792
18793 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
18794
18795 for (var _i2 = 0; _i2 < fields.length; _i2++) {
18796 var f = fields[_i2];
18797
18798 if (obj[f] != null) {
18799 cy[f](obj[f]);
18800 }
18801 }
18802
18803 cy.endBatch();
18804 return this; // chaining
18805 } else {
18806 // get
18807 var flat = !!obj;
18808 var json = {};
18809
18810 if (flat) {
18811 json.elements = this.elements().map(function (ele) {
18812 return ele.json();
18813 });
18814 } else {
18815 json.elements = {};
18816 eles.forEach(function (ele) {
18817 var group = ele.group();
18818
18819 if (!json.elements[group]) {
18820 json.elements[group] = [];
18821 }
18822
18823 json.elements[group].push(ele.json());
18824 });
18825 }
18826
18827 if (this._private.styleEnabled) {
18828 json.style = cy.style().json();
18829 }
18830
18831 json.data = copy(cy.data());
18832 var options = _p.options;
18833 json.zoomingEnabled = _p.zoomingEnabled;
18834 json.userZoomingEnabled = _p.userZoomingEnabled;
18835 json.zoom = _p.zoom;
18836 json.minZoom = _p.minZoom;
18837 json.maxZoom = _p.maxZoom;
18838 json.panningEnabled = _p.panningEnabled;
18839 json.userPanningEnabled = _p.userPanningEnabled;
18840 json.pan = copy(_p.pan);
18841 json.boxSelectionEnabled = _p.boxSelectionEnabled;
18842 json.renderer = copy(options.renderer);
18843 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
18844 json.textureOnViewport = options.textureOnViewport;
18845 json.wheelSensitivity = options.wheelSensitivity;
18846 json.motionBlur = options.motionBlur;
18847 return json;
18848 }
18849 }
18850});
18851corefn$9.$id = corefn$9.getElementById;
18852[corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
18853 extend(corefn$9, props);
18854});
18855
18856/* eslint-disable no-unused-vars */
18857
18858var defaults$9 = {
18859 fit: true,
18860 // whether to fit the viewport to the graph
18861 directed: false,
18862 // whether the tree is directed downwards (or edges can point in any direction if false)
18863 padding: 30,
18864 // padding on fit
18865 circle: false,
18866 // put depths in concentric circles if true, put depths top down if false
18867 grid: false,
18868 // whether to create an even grid into which the DAG is placed (circle:false only)
18869 spacingFactor: 1.75,
18870 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
18871 boundingBox: undefined,
18872 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
18873 avoidOverlap: true,
18874 // prevents node overlap, may overflow boundingBox if not enough space
18875 nodeDimensionsIncludeLabels: false,
18876 // Excludes the label when calculating node bounding boxes for the layout algorithm
18877 roots: undefined,
18878 // the roots of the trees
18879 maximal: false,
18880 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
18881 animate: false,
18882 // whether to transition the node positions
18883 animationDuration: 500,
18884 // duration of animation in ms if enabled
18885 animationEasing: undefined,
18886 // easing of animation if enabled,
18887 animateFilter: function animateFilter(node, i) {
18888 return true;
18889 },
18890 // 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
18891 ready: undefined,
18892 // callback on layoutready
18893 stop: undefined,
18894 // callback on layoutstop
18895 transform: function transform(node, position) {
18896 return position;
18897 } // transform a given node position. Useful for changing flow direction in discrete layouts
18898
18899};
18900/* eslint-enable */
18901
18902var getInfo = function getInfo(ele) {
18903 return ele.scratch('breadthfirst');
18904};
18905
18906var setInfo = function setInfo(ele, obj) {
18907 return ele.scratch('breadthfirst', obj);
18908};
18909
18910function BreadthFirstLayout(options) {
18911 this.options = extend({}, defaults$9, options);
18912}
18913
18914BreadthFirstLayout.prototype.run = function () {
18915 var params = this.options;
18916 var options = params;
18917 var cy = params.cy;
18918 var eles = options.eles;
18919 var nodes = eles.nodes().filter(function (n) {
18920 return !n.isParent();
18921 });
18922 var graph = eles;
18923 var directed = options.directed;
18924 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
18925
18926 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
18927 x1: 0,
18928 y1: 0,
18929 w: cy.width(),
18930 h: cy.height()
18931 });
18932 var roots;
18933
18934 if (elementOrCollection(options.roots)) {
18935 roots = options.roots;
18936 } else if (array(options.roots)) {
18937 var rootsArray = [];
18938
18939 for (var i = 0; i < options.roots.length; i++) {
18940 var id = options.roots[i];
18941 var ele = cy.getElementById(id);
18942 rootsArray.push(ele);
18943 }
18944
18945 roots = cy.collection(rootsArray);
18946 } else if (string(options.roots)) {
18947 roots = cy.$(options.roots);
18948 } else {
18949 if (directed) {
18950 roots = nodes.roots();
18951 } else {
18952 var components = eles.components();
18953 roots = cy.collection();
18954
18955 var _loop = function _loop(_i) {
18956 var comp = components[_i];
18957 var maxDegree = comp.maxDegree(false);
18958 var compRoots = comp.filter(function (ele) {
18959 return ele.degree(false) === maxDegree;
18960 });
18961 roots = roots.add(compRoots);
18962 };
18963
18964 for (var _i = 0; _i < components.length; _i++) {
18965 _loop(_i);
18966 }
18967 }
18968 }
18969
18970 var depths = [];
18971 var foundByBfs = {};
18972
18973 var addToDepth = function addToDepth(ele, d) {
18974 if (depths[d] == null) {
18975 depths[d] = [];
18976 }
18977
18978 var i = depths[d].length;
18979 depths[d].push(ele);
18980 setInfo(ele, {
18981 index: i,
18982 depth: d
18983 });
18984 };
18985
18986 var changeDepth = function changeDepth(ele, newDepth) {
18987 var _getInfo = getInfo(ele),
18988 depth = _getInfo.depth,
18989 index = _getInfo.index;
18990
18991 depths[depth][index] = null;
18992 addToDepth(ele, newDepth);
18993 }; // find the depths of the nodes
18994
18995
18996 graph.bfs({
18997 roots: roots,
18998 directed: options.directed,
18999 visit: function visit(node, edge, pNode, i, depth) {
19000 var ele = node[0];
19001 var id = ele.id();
19002 addToDepth(ele, depth);
19003 foundByBfs[id] = true;
19004 }
19005 }); // check for nodes not found by bfs
19006
19007 var orphanNodes = [];
19008
19009 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19010 var _ele = nodes[_i2];
19011
19012 if (foundByBfs[_ele.id()]) {
19013 continue;
19014 } else {
19015 orphanNodes.push(_ele);
19016 }
19017 } // assign the nodes a depth and index
19018
19019
19020 var assignDepthsAt = function assignDepthsAt(i) {
19021 var eles = depths[i];
19022
19023 for (var j = 0; j < eles.length; j++) {
19024 var _ele2 = eles[j];
19025
19026 if (_ele2 == null) {
19027 eles.splice(j, 1);
19028 j--;
19029 continue;
19030 }
19031
19032 setInfo(_ele2, {
19033 depth: i,
19034 index: j
19035 });
19036 }
19037 };
19038
19039 var assignDepths = function assignDepths() {
19040 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19041 assignDepthsAt(_i3);
19042 }
19043 };
19044
19045 var adjustMaximally = function adjustMaximally(ele, shifted) {
19046 var eInfo = getInfo(ele);
19047 var incomers = ele.incomers().filter(function (el) {
19048 return el.isNode() && eles.has(el);
19049 });
19050 var maxDepth = -1;
19051 var id = ele.id();
19052
19053 for (var k = 0; k < incomers.length; k++) {
19054 var incmr = incomers[k];
19055 var iInfo = getInfo(incmr);
19056 maxDepth = Math.max(maxDepth, iInfo.depth);
19057 }
19058
19059 if (eInfo.depth <= maxDepth) {
19060 if (shifted[id]) {
19061 return null;
19062 }
19063
19064 changeDepth(ele, maxDepth + 1);
19065 shifted[id] = true;
19066 return true;
19067 }
19068
19069 return false;
19070 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19071
19072
19073 if (directed && maximal) {
19074 var Q = [];
19075 var shifted = {};
19076
19077 var enqueue = function enqueue(n) {
19078 return Q.push(n);
19079 };
19080
19081 var dequeue = function dequeue() {
19082 return Q.shift();
19083 };
19084
19085 nodes.forEach(function (n) {
19086 return Q.push(n);
19087 });
19088
19089 while (Q.length > 0) {
19090 var _ele3 = dequeue();
19091
19092 var didShift = adjustMaximally(_ele3, shifted);
19093
19094 if (didShift) {
19095 _ele3.outgoers().filter(function (el) {
19096 return el.isNode() && eles.has(el);
19097 }).forEach(enqueue);
19098 } else if (didShift === null) {
19099 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19100 break; // exit on failure
19101 }
19102 }
19103 }
19104
19105 assignDepths(); // clear holes
19106 // find min distance we need to leave between nodes
19107
19108 var minDistance = 0;
19109
19110 if (options.avoidOverlap) {
19111 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19112 var n = nodes[_i4];
19113 var nbb = n.layoutDimensions(options);
19114 var w = nbb.w;
19115 var h = nbb.h;
19116 minDistance = Math.max(minDistance, w, h);
19117 }
19118 } // get the weighted percent for an element based on its connectivity to other levels
19119
19120
19121 var cachedWeightedPercent = {};
19122
19123 var getWeightedPercent = function getWeightedPercent(ele) {
19124 if (cachedWeightedPercent[ele.id()]) {
19125 return cachedWeightedPercent[ele.id()];
19126 }
19127
19128 var eleDepth = getInfo(ele).depth;
19129 var neighbors = ele.neighborhood();
19130 var percent = 0;
19131 var samples = 0;
19132
19133 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19134 var neighbor = neighbors[_i5];
19135
19136 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19137 continue;
19138 }
19139
19140 var bf = getInfo(neighbor);
19141 var index = bf.index;
19142 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19143
19144 if (index == null || depth == null) {
19145 continue;
19146 }
19147
19148 var nDepth = depths[depth].length;
19149
19150 if (depth < eleDepth) {
19151 // only get influenced by elements above
19152 percent += index / nDepth;
19153 samples++;
19154 }
19155 }
19156
19157 samples = Math.max(1, samples);
19158 percent = percent / samples;
19159
19160 if (samples === 0) {
19161 // put lone nodes at the start
19162 percent = 0;
19163 }
19164
19165 cachedWeightedPercent[ele.id()] = percent;
19166 return percent;
19167 }; // rearrange the indices in each depth level based on connectivity
19168
19169
19170 var sortFn = function sortFn(a, b) {
19171 var apct = getWeightedPercent(a);
19172 var bpct = getWeightedPercent(b);
19173 var diff = apct - bpct;
19174
19175 if (diff === 0) {
19176 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19177 } else {
19178 return diff;
19179 }
19180 }; // sort each level to make connected nodes closer
19181
19182
19183 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19184 depths[_i6].sort(sortFn);
19185
19186 assignDepthsAt(_i6);
19187 } // assign orphan nodes to a new top-level depth
19188
19189
19190 var orphanDepth = [];
19191
19192 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19193 orphanDepth.push(orphanNodes[_i7]);
19194 }
19195
19196 depths.unshift(orphanDepth);
19197 assignDepths();
19198 var biggestDepthSize = 0;
19199
19200 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19201 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19202 }
19203
19204 var center = {
19205 x: bb.x1 + bb.w / 2,
19206 y: bb.x1 + bb.h / 2
19207 };
19208 var maxDepthSize = depths.reduce(function (max, eles) {
19209 return Math.max(max, eles.length);
19210 }, 0);
19211
19212 var getPosition = function getPosition(ele) {
19213 var _getInfo2 = getInfo(ele),
19214 depth = _getInfo2.depth,
19215 index = _getInfo2.index;
19216
19217 var depthSize = depths[depth].length;
19218 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19219 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19220 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19221 radiusStepSize = Math.max(radiusStepSize, minDistance);
19222
19223 if (!options.circle) {
19224 var epos = {
19225 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19226 y: (depth + 1) * distanceY
19227 };
19228 return epos;
19229 } else {
19230 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19231 var theta = 2 * Math.PI / depths[depth].length * index;
19232
19233 if (depth === 0 && depths[0].length === 1) {
19234 radius = 1;
19235 }
19236
19237 return {
19238 x: center.x + radius * Math.cos(theta),
19239 y: center.y + radius * Math.sin(theta)
19240 };
19241 }
19242 };
19243
19244 nodes.layoutPositions(this, options, getPosition);
19245 return this; // chaining
19246};
19247
19248var defaults$a = {
19249 fit: true,
19250 // whether to fit the viewport to the graph
19251 padding: 30,
19252 // the padding on fit
19253 boundingBox: undefined,
19254 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19255 avoidOverlap: true,
19256 // prevents node overlap, may overflow boundingBox and radius if not enough space
19257 nodeDimensionsIncludeLabels: false,
19258 // Excludes the label when calculating node bounding boxes for the layout algorithm
19259 spacingFactor: undefined,
19260 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19261 radius: undefined,
19262 // the radius of the circle
19263 startAngle: 3 / 2 * Math.PI,
19264 // where nodes start in radians
19265 sweep: undefined,
19266 // how many radians should be between the first and last node (defaults to full circle)
19267 clockwise: true,
19268 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19269 sort: undefined,
19270 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19271 animate: false,
19272 // whether to transition the node positions
19273 animationDuration: 500,
19274 // duration of animation in ms if enabled
19275 animationEasing: undefined,
19276 // easing of animation if enabled
19277 animateFilter: function animateFilter(node, i) {
19278 return true;
19279 },
19280 // 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
19281 ready: undefined,
19282 // callback on layoutready
19283 stop: undefined,
19284 // callback on layoutstop
19285 transform: function transform(node, position) {
19286 return position;
19287 } // transform a given node position. Useful for changing flow direction in discrete layouts
19288
19289};
19290
19291function CircleLayout(options) {
19292 this.options = extend({}, defaults$a, options);
19293}
19294
19295CircleLayout.prototype.run = function () {
19296 var params = this.options;
19297 var options = params;
19298 var cy = params.cy;
19299 var eles = options.eles;
19300 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19301 var nodes = eles.nodes().not(':parent');
19302
19303 if (options.sort) {
19304 nodes = nodes.sort(options.sort);
19305 }
19306
19307 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19308 x1: 0,
19309 y1: 0,
19310 w: cy.width(),
19311 h: cy.height()
19312 });
19313 var center = {
19314 x: bb.x1 + bb.w / 2,
19315 y: bb.y1 + bb.h / 2
19316 };
19317 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19318 var dTheta = sweep / Math.max(1, nodes.length - 1);
19319 var r;
19320 var minDistance = 0;
19321
19322 for (var i = 0; i < nodes.length; i++) {
19323 var n = nodes[i];
19324 var nbb = n.layoutDimensions(options);
19325 var w = nbb.w;
19326 var h = nbb.h;
19327 minDistance = Math.max(minDistance, w, h);
19328 }
19329
19330 if (number(options.radius)) {
19331 r = options.radius;
19332 } else if (nodes.length <= 1) {
19333 r = 0;
19334 } else {
19335 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19336 } // calculate the radius
19337
19338
19339 if (nodes.length > 1 && options.avoidOverlap) {
19340 // but only if more than one node (can't overlap)
19341 minDistance *= 1.75; // just to have some nice spacing
19342
19343 var dcos = Math.cos(dTheta) - Math.cos(0);
19344 var dsin = Math.sin(dTheta) - Math.sin(0);
19345 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19346
19347 r = Math.max(rMin, r);
19348 }
19349
19350 var getPos = function getPos(ele, i) {
19351 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19352 var rx = r * Math.cos(theta);
19353 var ry = r * Math.sin(theta);
19354 var pos = {
19355 x: center.x + rx,
19356 y: center.y + ry
19357 };
19358 return pos;
19359 };
19360
19361 nodes.layoutPositions(this, options, getPos);
19362 return this; // chaining
19363};
19364
19365var defaults$b = {
19366 fit: true,
19367 // whether to fit the viewport to the graph
19368 padding: 30,
19369 // the padding on fit
19370 startAngle: 3 / 2 * Math.PI,
19371 // where nodes start in radians
19372 sweep: undefined,
19373 // how many radians should be between the first and last node (defaults to full circle)
19374 clockwise: true,
19375 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19376 equidistant: false,
19377 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19378 minNodeSpacing: 10,
19379 // min spacing between outside of nodes (used for radius adjustment)
19380 boundingBox: undefined,
19381 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19382 avoidOverlap: true,
19383 // prevents node overlap, may overflow boundingBox if not enough space
19384 nodeDimensionsIncludeLabels: false,
19385 // Excludes the label when calculating node bounding boxes for the layout algorithm
19386 height: undefined,
19387 // height of layout area (overrides container height)
19388 width: undefined,
19389 // width of layout area (overrides container width)
19390 spacingFactor: undefined,
19391 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19392 concentric: function concentric(node) {
19393 // returns numeric value for each node, placing higher nodes in levels towards the centre
19394 return node.degree();
19395 },
19396 levelWidth: function levelWidth(nodes) {
19397 // the letiation of concentric values in each level
19398 return nodes.maxDegree() / 4;
19399 },
19400 animate: false,
19401 // whether to transition the node positions
19402 animationDuration: 500,
19403 // duration of animation in ms if enabled
19404 animationEasing: undefined,
19405 // easing of animation if enabled
19406 animateFilter: function animateFilter(node, i) {
19407 return true;
19408 },
19409 // 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
19410 ready: undefined,
19411 // callback on layoutready
19412 stop: undefined,
19413 // callback on layoutstop
19414 transform: function transform(node, position) {
19415 return position;
19416 } // transform a given node position. Useful for changing flow direction in discrete layouts
19417
19418};
19419
19420function ConcentricLayout(options) {
19421 this.options = extend({}, defaults$b, options);
19422}
19423
19424ConcentricLayout.prototype.run = function () {
19425 var params = this.options;
19426 var options = params;
19427 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19428 var cy = params.cy;
19429 var eles = options.eles;
19430 var nodes = eles.nodes().not(':parent');
19431 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19432 x1: 0,
19433 y1: 0,
19434 w: cy.width(),
19435 h: cy.height()
19436 });
19437 var center = {
19438 x: bb.x1 + bb.w / 2,
19439 y: bb.y1 + bb.h / 2
19440 };
19441 var nodeValues = []; // { node, value }
19442
19443 var maxNodeSize = 0;
19444
19445 for (var i = 0; i < nodes.length; i++) {
19446 var node = nodes[i];
19447 var value = void 0; // calculate the node value
19448
19449 value = options.concentric(node);
19450 nodeValues.push({
19451 value: value,
19452 node: node
19453 }); // for style mapping
19454
19455 node._private.scratch.concentric = value;
19456 } // in case we used the `concentric` in style
19457
19458
19459 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
19460
19461 for (var _i = 0; _i < nodes.length; _i++) {
19462 var _node = nodes[_i];
19463
19464 var nbb = _node.layoutDimensions(options);
19465
19466 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19467 } // sort node values in descreasing order
19468
19469
19470 nodeValues.sort(function (a, b) {
19471 return b.value - a.value;
19472 });
19473 var levelWidth = options.levelWidth(nodes); // put the values into levels
19474
19475 var levels = [[]];
19476 var currentLevel = levels[0];
19477
19478 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19479 var val = nodeValues[_i2];
19480
19481 if (currentLevel.length > 0) {
19482 var diff = Math.abs(currentLevel[0].value - val.value);
19483
19484 if (diff >= levelWidth) {
19485 currentLevel = [];
19486 levels.push(currentLevel);
19487 }
19488 }
19489
19490 currentLevel.push(val);
19491 } // create positions from levels
19492
19493
19494 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19495
19496 if (!options.avoidOverlap) {
19497 // then strictly constrain to bb
19498 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19499 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19500 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19501 minDist = Math.min(minDist, rStep);
19502 } // find the metrics for each level
19503
19504
19505 var r = 0;
19506
19507 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19508 var level = levels[_i3];
19509 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19510 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
19511
19512 if (level.length > 1 && options.avoidOverlap) {
19513 // but only if more than one node (can't overlap)
19514 var dcos = Math.cos(dTheta) - Math.cos(0);
19515 var dsin = Math.sin(dTheta) - Math.sin(0);
19516 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19517
19518 r = Math.max(rMin, r);
19519 }
19520
19521 level.r = r;
19522 r += minDist;
19523 }
19524
19525 if (options.equidistant) {
19526 var rDeltaMax = 0;
19527 var _r = 0;
19528
19529 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19530 var _level = levels[_i4];
19531 var rDelta = _level.r - _r;
19532 rDeltaMax = Math.max(rDeltaMax, rDelta);
19533 }
19534
19535 _r = 0;
19536
19537 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19538 var _level2 = levels[_i5];
19539
19540 if (_i5 === 0) {
19541 _r = _level2.r;
19542 }
19543
19544 _level2.r = _r;
19545 _r += rDeltaMax;
19546 }
19547 } // calculate the node positions
19548
19549
19550 var pos = {}; // id => position
19551
19552 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19553 var _level3 = levels[_i6];
19554 var _dTheta = _level3.dTheta;
19555 var _r2 = _level3.r;
19556
19557 for (var j = 0; j < _level3.length; j++) {
19558 var _val = _level3[j];
19559 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19560 var p = {
19561 x: center.x + _r2 * Math.cos(theta),
19562 y: center.y + _r2 * Math.sin(theta)
19563 };
19564 pos[_val.node.id()] = p;
19565 }
19566 } // position the nodes
19567
19568
19569 nodes.layoutPositions(this, options, function (ele) {
19570 var id = ele.id();
19571 return pos[id];
19572 });
19573 return this; // chaining
19574};
19575
19576/*
19577The CoSE layout was written by Gerardo Huck.
19578https://www.linkedin.com/in/gerardohuck/
19579
19580Based on the following article:
19581http://dl.acm.org/citation.cfm?id=1498047
19582
19583Modifications tracked on Github.
19584*/
19585var DEBUG;
19586/**
19587 * @brief : default layout options
19588 */
19589
19590var defaults$c = {
19591 // Called on `layoutready`
19592 ready: function ready() {},
19593 // Called on `layoutstop`
19594 stop: function stop() {},
19595 // Whether to animate while running the layout
19596 // true : Animate continuously as the layout is running
19597 // false : Just show the end result
19598 // 'end' : Animate with the end result, from the initial positions to the end positions
19599 animate: true,
19600 // Easing of the animation for animate:'end'
19601 animationEasing: undefined,
19602 // The duration of the animation for animate:'end'
19603 animationDuration: undefined,
19604 // A function that determines whether the node should be animated
19605 // All nodes animated by default on animate enabled
19606 // Non-animated nodes are positioned immediately when the layout starts
19607 animateFilter: function animateFilter(node, i) {
19608 return true;
19609 },
19610 // The layout animates only after this many milliseconds for animate:true
19611 // (prevents flashing on fast runs)
19612 animationThreshold: 250,
19613 // Number of iterations between consecutive screen positions update
19614 refresh: 20,
19615 // Whether to fit the network view after when done
19616 fit: true,
19617 // Padding on fit
19618 padding: 30,
19619 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19620 boundingBox: undefined,
19621 // Excludes the label when calculating node bounding boxes for the layout algorithm
19622 nodeDimensionsIncludeLabels: false,
19623 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19624 randomize: false,
19625 // Extra spacing between components in non-compound graphs
19626 componentSpacing: 40,
19627 // Node repulsion (non overlapping) multiplier
19628 nodeRepulsion: function nodeRepulsion(node) {
19629 return 2048;
19630 },
19631 // Node repulsion (overlapping) multiplier
19632 nodeOverlap: 4,
19633 // Ideal edge (non nested) length
19634 idealEdgeLength: function idealEdgeLength(edge) {
19635 return 32;
19636 },
19637 // Divisor to compute edge forces
19638 edgeElasticity: function edgeElasticity(edge) {
19639 return 32;
19640 },
19641 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19642 nestingFactor: 1.2,
19643 // Gravity force (constant)
19644 gravity: 1,
19645 // Maximum number of iterations to perform
19646 numIter: 1000,
19647 // Initial temperature (maximum node displacement)
19648 initialTemp: 1000,
19649 // Cooling factor (how the temperature is reduced between consecutive iterations
19650 coolingFactor: 0.99,
19651 // Lower temperature threshold (below this point the layout will end)
19652 minTemp: 1.0
19653};
19654/**
19655 * @brief : constructor
19656 * @arg options : object containing layout options
19657 */
19658
19659function CoseLayout(options) {
19660 this.options = extend({}, defaults$c, options);
19661 this.options.layout = this;
19662}
19663/**
19664 * @brief : runs the layout
19665 */
19666
19667
19668CoseLayout.prototype.run = function () {
19669 var options = this.options;
19670 var cy = options.cy;
19671 var layout = this;
19672 layout.stopped = false;
19673
19674 if (options.animate === true || options.animate === false) {
19675 layout.emit({
19676 type: 'layoutstart',
19677 layout: layout
19678 });
19679 } // Set DEBUG - Global variable
19680
19681
19682 if (true === options.debug) {
19683 DEBUG = true;
19684 } else {
19685 DEBUG = false;
19686 } // Initialize layout info
19687
19688
19689 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
19690
19691 if (DEBUG) {
19692 printLayoutInfo(layoutInfo);
19693 } // If required, randomize node positions
19694
19695
19696 if (options.randomize) {
19697 randomizePositions(layoutInfo);
19698 }
19699
19700 var startTime = performanceNow();
19701
19702 var refresh = function refresh() {
19703 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
19704
19705 if (true === options.fit) {
19706 cy.fit(options.padding);
19707 }
19708 };
19709
19710 var mainLoop = function mainLoop(i) {
19711 if (layout.stopped || i >= options.numIter) {
19712 // logDebug("Layout manually stopped. Stopping computation in step " + i);
19713 return false;
19714 } // Do one step in the phisical simulation
19715
19716
19717 step$1(layoutInfo, options); // Update temperature
19718
19719 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
19720
19721 if (layoutInfo.temperature < options.minTemp) {
19722 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
19723 return false;
19724 }
19725
19726 return true;
19727 };
19728
19729 var done = function done() {
19730 if (options.animate === true || options.animate === false) {
19731 refresh(); // Layout has finished
19732
19733 layout.one('layoutstop', options.stop);
19734 layout.emit({
19735 type: 'layoutstop',
19736 layout: layout
19737 });
19738 } else {
19739 var nodes = options.eles.nodes();
19740 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19741 nodes.layoutPositions(layout, options, getScaledPos);
19742 }
19743 };
19744
19745 var i = 0;
19746 var loopRet = true;
19747
19748 if (options.animate === true) {
19749 var frame = function frame() {
19750 var f = 0;
19751
19752 while (loopRet && f < options.refresh) {
19753 loopRet = mainLoop(i);
19754 i++;
19755 f++;
19756 }
19757
19758 if (!loopRet) {
19759 // it's done
19760 separateComponents(layoutInfo, options);
19761 done();
19762 } else {
19763 var now = performanceNow();
19764
19765 if (now - startTime >= options.animationThreshold) {
19766 refresh();
19767 }
19768
19769 requestAnimationFrame(frame);
19770 }
19771 };
19772
19773 frame();
19774 } else {
19775 while (loopRet) {
19776 loopRet = mainLoop(i);
19777 i++;
19778 }
19779
19780 separateComponents(layoutInfo, options);
19781 done();
19782 }
19783
19784 return this; // chaining
19785};
19786/**
19787 * @brief : called on continuous layouts to stop them before they finish
19788 */
19789
19790
19791CoseLayout.prototype.stop = function () {
19792 this.stopped = true;
19793
19794 if (this.thread) {
19795 this.thread.stop();
19796 }
19797
19798 this.emit('layoutstop');
19799 return this; // chaining
19800};
19801
19802CoseLayout.prototype.destroy = function () {
19803 if (this.thread) {
19804 this.thread.stop();
19805 }
19806
19807 return this; // chaining
19808};
19809/**
19810 * @brief : Creates an object which is contains all the data
19811 * used in the layout process
19812 * @arg cy : cytoscape.js object
19813 * @return : layoutInfo object initialized
19814 */
19815
19816
19817var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
19818 // Shortcut
19819 var edges = options.eles.edges();
19820 var nodes = options.eles.nodes();
19821 var layoutInfo = {
19822 isCompound: cy.hasCompoundNodes(),
19823 layoutNodes: [],
19824 idToIndex: {},
19825 nodeSize: nodes.size(),
19826 graphSet: [],
19827 indexToGraph: [],
19828 layoutEdges: [],
19829 edgeSize: edges.size(),
19830 temperature: options.initialTemp,
19831 clientWidth: cy.width(),
19832 clientHeight: cy.width(),
19833 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
19834 x1: 0,
19835 y1: 0,
19836 w: cy.width(),
19837 h: cy.height()
19838 })
19839 };
19840 var components = options.eles.components();
19841 var id2cmptId = {};
19842
19843 for (var i = 0; i < components.length; i++) {
19844 var component = components[i];
19845
19846 for (var j = 0; j < component.length; j++) {
19847 var node = component[j];
19848 id2cmptId[node.id()] = i;
19849 }
19850 } // Iterate over all nodes, creating layout nodes
19851
19852
19853 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19854 var n = nodes[i];
19855 var nbb = n.layoutDimensions(options);
19856 var tempNode = {};
19857 tempNode.isLocked = n.locked();
19858 tempNode.id = n.data('id');
19859 tempNode.parentId = n.data('parent');
19860 tempNode.cmptId = id2cmptId[n.id()];
19861 tempNode.children = [];
19862 tempNode.positionX = n.position('x');
19863 tempNode.positionY = n.position('y');
19864 tempNode.offsetX = 0;
19865 tempNode.offsetY = 0;
19866 tempNode.height = nbb.w;
19867 tempNode.width = nbb.h;
19868 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
19869 tempNode.minX = tempNode.positionX - tempNode.width / 2;
19870 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
19871 tempNode.minY = tempNode.positionY - tempNode.height / 2;
19872 tempNode.padLeft = parseFloat(n.style('padding'));
19873 tempNode.padRight = parseFloat(n.style('padding'));
19874 tempNode.padTop = parseFloat(n.style('padding'));
19875 tempNode.padBottom = parseFloat(n.style('padding')); // forces
19876
19877 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
19878
19879 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
19880
19881 layoutInfo.idToIndex[tempNode.id] = i;
19882 } // Inline implementation of a queue, used for traversing the graph in BFS order
19883
19884
19885 var queue = [];
19886 var start = 0; // Points to the start the queue
19887
19888 var end = -1; // Points to the end of the queue
19889
19890 var tempGraph = []; // Second pass to add child information and
19891 // initialize queue for hierarchical traversal
19892
19893 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19894 var n = layoutInfo.layoutNodes[i];
19895 var p_id = n.parentId; // Check if node n has a parent node
19896
19897 if (null != p_id) {
19898 // Add node Id to parent's list of children
19899 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
19900 } else {
19901 // If a node doesn't have a parent, then it's in the root graph
19902 queue[++end] = n.id;
19903 tempGraph.push(n.id);
19904 }
19905 } // Add root graph to graphSet
19906
19907
19908 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
19909
19910 while (start <= end) {
19911 // Get the node to visit and remove it from queue
19912 var node_id = queue[start++];
19913 var node_ix = layoutInfo.idToIndex[node_id];
19914 var node = layoutInfo.layoutNodes[node_ix];
19915 var children = node.children;
19916
19917 if (children.length > 0) {
19918 // Add children nodes as a new graph to graph set
19919 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
19920
19921 for (var i = 0; i < children.length; i++) {
19922 queue[++end] = children[i];
19923 }
19924 }
19925 } // Create indexToGraph map
19926
19927
19928 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
19929 var graph = layoutInfo.graphSet[i];
19930
19931 for (var j = 0; j < graph.length; j++) {
19932 var index = layoutInfo.idToIndex[graph[j]];
19933 layoutInfo.indexToGraph[index] = i;
19934 }
19935 } // Iterate over all edges, creating Layout Edges
19936
19937
19938 for (var i = 0; i < layoutInfo.edgeSize; i++) {
19939 var e = edges[i];
19940 var tempEdge = {};
19941 tempEdge.id = e.data('id');
19942 tempEdge.sourceId = e.data('source');
19943 tempEdge.targetId = e.data('target'); // Compute ideal length
19944
19945 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
19946 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
19947
19948 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
19949 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
19950 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
19951 var targetGraph = layoutInfo.indexToGraph[targetIx];
19952
19953 if (sourceGraph != targetGraph) {
19954 // Find lowest common graph ancestor
19955 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
19956
19957 var lcaGraph = layoutInfo.graphSet[lca];
19958 var depth = 0; // Source depth
19959
19960 var tempNode = layoutInfo.layoutNodes[sourceIx];
19961
19962 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19963 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19964 depth++;
19965 } // Target depth
19966
19967
19968 tempNode = layoutInfo.layoutNodes[targetIx];
19969
19970 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19971 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19972 depth++;
19973 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
19974 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
19975 // ". Depth: " + depth);
19976 // Update idealLength
19977
19978
19979 idealLength *= depth * options.nestingFactor;
19980 }
19981
19982 tempEdge.idealLength = idealLength;
19983 tempEdge.elasticity = elasticity;
19984 layoutInfo.layoutEdges.push(tempEdge);
19985 } // Finally, return layoutInfo object
19986
19987
19988 return layoutInfo;
19989};
19990/**
19991 * @brief : This function finds the index of the lowest common
19992 * graph ancestor between 2 nodes in the subtree
19993 * (from the graph hierarchy induced tree) whose
19994 * root is graphIx
19995 *
19996 * @arg node1: node1's ID
19997 * @arg node2: node2's ID
19998 * @arg layoutInfo: layoutInfo object
19999 *
20000 */
20001
20002
20003var findLCA = function findLCA(node1, node2, layoutInfo) {
20004 // Find their common ancester, starting from the root graph
20005 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20006
20007 if (2 > res.count) {
20008 // If aux function couldn't find the common ancester,
20009 // then it is the root graph
20010 return 0;
20011 } else {
20012 return res.graph;
20013 }
20014};
20015/**
20016 * @brief : Auxiliary function used for LCA computation
20017 *
20018 * @arg node1 : node1's ID
20019 * @arg node2 : node2's ID
20020 * @arg graphIx : subgraph index
20021 * @arg layoutInfo : layoutInfo object
20022 *
20023 * @return : object of the form {count: X, graph: Y}, where:
20024 * X is the number of ancesters (max: 2) found in
20025 * graphIx (and it's subgraphs),
20026 * Y is the graph index of the lowest graph containing
20027 * all X nodes
20028 */
20029
20030
20031var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20032 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20033
20034 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20035 return {
20036 count: 2,
20037 graph: graphIx
20038 };
20039 } // Make recursive calls for all subgraphs
20040
20041
20042 var c = 0;
20043
20044 for (var i = 0; i < graph.length; i++) {
20045 var nodeId = graph[i];
20046 var nodeIx = layoutInfo.idToIndex[nodeId];
20047 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20048
20049 if (0 === children.length) {
20050 continue;
20051 }
20052
20053 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20054 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20055
20056 if (0 === result.count) {
20057 // Neither node1 nor node2 are present in this subgraph
20058 continue;
20059 } else if (1 === result.count) {
20060 // One of (node1, node2) is present in this subgraph
20061 c++;
20062
20063 if (2 === c) {
20064 // We've already found both nodes, no need to keep searching
20065 break;
20066 }
20067 } else {
20068 // Both nodes are present in this subgraph
20069 return result;
20070 }
20071 }
20072
20073 return {
20074 count: c,
20075 graph: graphIx
20076 };
20077};
20078/**
20079 * @brief: printsLayoutInfo into js console
20080 * Only used for debbuging
20081 */
20082
20083
20084if (false) {
20085 var printLayoutInfo;
20086}
20087/**
20088 * @brief : Randomizes the position of all nodes
20089 */
20090
20091
20092var randomizePositions = function randomizePositions(layoutInfo, cy) {
20093 var width = layoutInfo.clientWidth;
20094 var height = layoutInfo.clientHeight;
20095
20096 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20097 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20098
20099 if (0 === n.children.length && !n.isLocked) {
20100 n.positionX = Math.random() * width;
20101 n.positionY = Math.random() * height;
20102 }
20103 }
20104};
20105
20106var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20107 var bb = layoutInfo.boundingBox;
20108 var coseBB = {
20109 x1: Infinity,
20110 x2: -Infinity,
20111 y1: Infinity,
20112 y2: -Infinity
20113 };
20114
20115 if (options.boundingBox) {
20116 nodes.forEach(function (node) {
20117 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20118 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20119 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20120 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20121 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20122 });
20123 coseBB.w = coseBB.x2 - coseBB.x1;
20124 coseBB.h = coseBB.y2 - coseBB.y1;
20125 }
20126
20127 return function (ele, i) {
20128 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20129
20130 if (options.boundingBox) {
20131 // then add extra bounding box constraint
20132 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20133 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20134 return {
20135 x: bb.x1 + pctX * bb.w,
20136 y: bb.y1 + pctY * bb.h
20137 };
20138 } else {
20139 return {
20140 x: lnode.positionX,
20141 y: lnode.positionY
20142 };
20143 }
20144 };
20145};
20146/**
20147 * @brief : Updates the positions of nodes in the network
20148 * @arg layoutInfo : LayoutInfo object
20149 * @arg cy : Cytoscape object
20150 * @arg options : Layout options
20151 */
20152
20153
20154var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20155 // var s = 'Refreshing positions';
20156 // logDebug(s);
20157 var layout = options.layout;
20158 var nodes = options.eles.nodes();
20159 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20160 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20161
20162 if (true !== layoutInfo.ready) {
20163 // s = 'Triggering layoutready';
20164 // logDebug(s);
20165 layoutInfo.ready = true;
20166 layout.one('layoutready', options.ready);
20167 layout.emit({
20168 type: 'layoutready',
20169 layout: this
20170 });
20171 }
20172};
20173/**
20174 * @brief : Logs a debug message in JS console, if DEBUG is ON
20175 */
20176// var logDebug = function(text) {
20177// if (DEBUG) {
20178// console.debug(text);
20179// }
20180// };
20181
20182/**
20183 * @brief : Performs one iteration of the physical simulation
20184 * @arg layoutInfo : LayoutInfo object already initialized
20185 * @arg cy : Cytoscape object
20186 * @arg options : Layout options
20187 */
20188
20189
20190var step$1 = function step(layoutInfo, options, _step) {
20191 // var s = "\n\n###############################";
20192 // s += "\nSTEP: " + step;
20193 // s += "\n###############################\n";
20194 // logDebug(s);
20195 // Calculate node repulsions
20196 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20197
20198 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20199
20200 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20201
20202 propagateForces(layoutInfo); // Update positions based on calculated forces
20203
20204 updatePositions(layoutInfo);
20205};
20206/**
20207 * @brief : Computes the node repulsion forces
20208 */
20209
20210
20211var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20212 // Go through each of the graphs in graphSet
20213 // Nodes only repel each other if they belong to the same graph
20214 // var s = 'calculateNodeForces';
20215 // logDebug(s);
20216 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20217 var graph = layoutInfo.graphSet[i];
20218 var numNodes = graph.length; // s = "Set: " + graph.toString();
20219 // logDebug(s);
20220 // Now get all the pairs of nodes
20221 // Only get each pair once, (A, B) = (B, A)
20222
20223 for (var j = 0; j < numNodes; j++) {
20224 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20225
20226 for (var k = j + 1; k < numNodes; k++) {
20227 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20228 nodeRepulsion(node1, node2, layoutInfo, options);
20229 }
20230 }
20231 }
20232};
20233
20234var randomDistance = function randomDistance(max) {
20235 return -max + 2 * max * Math.random();
20236};
20237/**
20238 * @brief : Compute the node repulsion forces between a pair of nodes
20239 */
20240
20241
20242var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20243 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20244 var cmptId1 = node1.cmptId;
20245 var cmptId2 = node2.cmptId;
20246
20247 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20248 return;
20249 } // Get direction of line connecting both node centers
20250
20251
20252 var directionX = node2.positionX - node1.positionX;
20253 var directionY = node2.positionY - node1.positionY;
20254 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20255 // If both centers are the same, apply a random force
20256
20257 if (0 === directionX && 0 === directionY) {
20258 directionX = randomDistance(maxRandDist);
20259 directionY = randomDistance(maxRandDist);
20260 }
20261
20262 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20263
20264 if (overlap > 0) {
20265 // s += "\nNodes DO overlap.";
20266 // s += "\nOverlap: " + overlap;
20267 // If nodes overlap, repulsion force is proportional
20268 // to the overlap
20269 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
20270
20271 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
20272
20273 var forceX = force * directionX / distance;
20274 var forceY = force * directionY / distance;
20275 } else {
20276 // s += "\nNodes do NOT overlap.";
20277 // If there's no overlap, force is inversely proportional
20278 // to squared distance
20279 // Get clipping points for both nodes
20280 var point1 = findClippingPoint(node1, directionX, directionY);
20281 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
20282
20283 var distanceX = point2.x - point1.x;
20284 var distanceY = point2.y - point1.y;
20285 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20286 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
20287 // Compute the module and components of the force vector
20288
20289 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20290 var forceX = force * distanceX / distance;
20291 var forceY = force * distanceY / distance;
20292 } // Apply force
20293
20294
20295 if (!node1.isLocked) {
20296 node1.offsetX -= forceX;
20297 node1.offsetY -= forceY;
20298 }
20299
20300 if (!node2.isLocked) {
20301 node2.offsetX += forceX;
20302 node2.offsetY += forceY;
20303 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20304 // logDebug(s);
20305
20306
20307 return;
20308};
20309/**
20310 * @brief : Determines whether two nodes overlap or not
20311 * @return : Amount of overlapping (0 => no overlap)
20312 */
20313
20314
20315var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20316 if (dX > 0) {
20317 var overlapX = node1.maxX - node2.minX;
20318 } else {
20319 var overlapX = node2.maxX - node1.minX;
20320 }
20321
20322 if (dY > 0) {
20323 var overlapY = node1.maxY - node2.minY;
20324 } else {
20325 var overlapY = node2.maxY - node1.minY;
20326 }
20327
20328 if (overlapX >= 0 && overlapY >= 0) {
20329 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20330 } else {
20331 return 0;
20332 }
20333};
20334/**
20335 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20336 * the rectangular bounding box of it's source/target node
20337 */
20338
20339
20340var findClippingPoint = function findClippingPoint(node, dX, dY) {
20341 // Shorcuts
20342 var X = node.positionX;
20343 var Y = node.positionY;
20344 var H = node.height || 1;
20345 var W = node.width || 1;
20346 var dirSlope = dY / dX;
20347 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
20348 // " . Height: " + H + ", Width: " + W +
20349 // "\nDirection " + dX + ", " + dY;
20350 //
20351 // Compute intersection
20352
20353 var res = {}; // Case: Vertical direction (up)
20354
20355 if (0 === dX && 0 < dY) {
20356 res.x = X; // s += "\nUp direction";
20357
20358 res.y = Y + H / 2;
20359 return res;
20360 } // Case: Vertical direction (down)
20361
20362
20363 if (0 === dX && 0 > dY) {
20364 res.x = X;
20365 res.y = Y + H / 2; // s += "\nDown direction";
20366
20367 return res;
20368 } // Case: Intersects the right border
20369
20370
20371 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20372 res.x = X + W / 2;
20373 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
20374
20375 return res;
20376 } // Case: Intersects the left border
20377
20378
20379 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20380 res.x = X - W / 2;
20381 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
20382
20383 return res;
20384 } // Case: Intersects the top border
20385
20386
20387 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20388 res.x = X + H * dX / 2 / dY;
20389 res.y = Y + H / 2; // s += "\nTop border";
20390
20391 return res;
20392 } // Case: Intersects the bottom border
20393
20394
20395 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20396 res.x = X - H * dX / 2 / dY;
20397 res.y = Y - H / 2; // s += "\nBottom border";
20398
20399 return res;
20400 } // s += "\nClipping point found at " + res.x + ", " + res.y;
20401 // logDebug(s);
20402
20403
20404 return res;
20405};
20406/**
20407 * @brief : Calculates all edge forces
20408 */
20409
20410
20411var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20412 // Iterate over all edges
20413 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20414 // Get edge, source & target nodes
20415 var edge = layoutInfo.layoutEdges[i];
20416 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20417 var source = layoutInfo.layoutNodes[sourceIx];
20418 var targetIx = layoutInfo.idToIndex[edge.targetId];
20419 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
20420
20421 var directionX = target.positionX - source.positionX;
20422 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
20423 // A random force has already been applied as node repulsion
20424
20425 if (0 === directionX && 0 === directionY) {
20426 continue;
20427 } // Get clipping points for both nodes
20428
20429
20430 var point1 = findClippingPoint(source, directionX, directionY);
20431 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20432 var lx = point2.x - point1.x;
20433 var ly = point2.y - point1.y;
20434 var l = Math.sqrt(lx * lx + ly * ly);
20435 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20436
20437 if (0 !== l) {
20438 var forceX = force * lx / l;
20439 var forceY = force * ly / l;
20440 } else {
20441 var forceX = 0;
20442 var forceY = 0;
20443 } // Add this force to target and source nodes
20444
20445
20446 if (!source.isLocked) {
20447 source.offsetX += forceX;
20448 source.offsetY += forceY;
20449 }
20450
20451 if (!target.isLocked) {
20452 target.offsetX -= forceX;
20453 target.offsetY -= forceY;
20454 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20455 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20456 // logDebug(s);
20457
20458 }
20459};
20460/**
20461 * @brief : Computes gravity forces for all nodes
20462 */
20463
20464
20465var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20466 var distThreshold = 1; // var s = 'calculateGravityForces';
20467 // logDebug(s);
20468
20469 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20470 var graph = layoutInfo.graphSet[i];
20471 var numNodes = graph.length; // s = "Set: " + graph.toString();
20472 // logDebug(s);
20473 // Compute graph center
20474
20475 if (0 === i) {
20476 var centerX = layoutInfo.clientHeight / 2;
20477 var centerY = layoutInfo.clientWidth / 2;
20478 } else {
20479 // Get Parent node for this graph, and use its position as center
20480 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20481 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20482 var centerX = parent.positionX;
20483 var centerY = parent.positionY;
20484 } // s = "Center found at: " + centerX + ", " + centerY;
20485 // logDebug(s);
20486 // Apply force to all nodes in graph
20487
20488
20489 for (var j = 0; j < numNodes; j++) {
20490 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
20491
20492 if (node.isLocked) {
20493 continue;
20494 }
20495
20496 var dx = centerX - node.positionX;
20497 var dy = centerY - node.positionY;
20498 var d = Math.sqrt(dx * dx + dy * dy);
20499
20500 if (d > distThreshold) {
20501 var fx = options.gravity * dx / d;
20502 var fy = options.gravity * dy / d;
20503 node.offsetX += fx;
20504 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
20505 } // s += ": skypped since it's too close to center";
20506 // logDebug(s);
20507
20508 }
20509 }
20510};
20511/**
20512 * @brief : This function propagates the existing offsets from
20513 * parent nodes to its descendents.
20514 * @arg layoutInfo : layoutInfo Object
20515 * @arg cy : cytoscape Object
20516 * @arg options : Layout options
20517 */
20518
20519
20520var propagateForces = function propagateForces(layoutInfo, options) {
20521 // Inline implementation of a queue, used for traversing the graph in BFS order
20522 var queue = [];
20523 var start = 0; // Points to the start the queue
20524
20525 var end = -1; // Points to the end of the queue
20526 // logDebug('propagateForces');
20527 // Start by visiting the nodes in the root graph
20528
20529 queue.push.apply(queue, layoutInfo.graphSet[0]);
20530 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
20531
20532 while (start <= end) {
20533 // Get the node to visit and remove it from queue
20534 var nodeId = queue[start++];
20535 var nodeIndex = layoutInfo.idToIndex[nodeId];
20536 var node = layoutInfo.layoutNodes[nodeIndex];
20537 var children = node.children; // We only need to process the node if it's compound
20538
20539 if (0 < children.length && !node.isLocked) {
20540 var offX = node.offsetX;
20541 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
20542 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20543 // s += "\n Children: " + children.toString();
20544 // logDebug(s);
20545
20546 for (var i = 0; i < children.length; i++) {
20547 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
20548
20549 childNode.offsetX += offX;
20550 childNode.offsetY += offY; // Add children to queue to be visited
20551
20552 queue[++end] = children[i];
20553 } // Reset parent offsets
20554
20555
20556 node.offsetX = 0;
20557 node.offsetY = 0;
20558 }
20559 }
20560};
20561/**
20562 * @brief : Updates the layout model positions, based on
20563 * the accumulated forces
20564 */
20565
20566
20567var updatePositions = function updatePositions(layoutInfo, options) {
20568 // var s = 'Updating positions';
20569 // logDebug(s);
20570 // Reset boundaries for compound nodes
20571 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20572 var n = layoutInfo.layoutNodes[i];
20573
20574 if (0 < n.children.length) {
20575 // logDebug("Resetting boundaries of compound node: " + n.id);
20576 n.maxX = undefined;
20577 n.minX = undefined;
20578 n.maxY = undefined;
20579 n.minY = undefined;
20580 }
20581 }
20582
20583 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20584 var n = layoutInfo.layoutNodes[i];
20585
20586 if (0 < n.children.length || n.isLocked) {
20587 // No need to set compound or locked node position
20588 // logDebug("Skipping position update of node: " + n.id);
20589 continue;
20590 } // s = "Node: " + n.id + " Previous position: (" +
20591 // n.positionX + ", " + n.positionY + ").";
20592 // Limit displacement in order to improve stability
20593
20594
20595 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20596 n.positionX += tempForce.x;
20597 n.positionY += tempForce.y;
20598 n.offsetX = 0;
20599 n.offsetY = 0;
20600 n.minX = n.positionX - n.width;
20601 n.maxX = n.positionX + n.width;
20602 n.minY = n.positionY - n.height;
20603 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20604 // logDebug(s);
20605 // Update ancestry boudaries
20606
20607 updateAncestryBoundaries(n, layoutInfo);
20608 } // Update size, position of compund nodes
20609
20610
20611 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20612 var n = layoutInfo.layoutNodes[i];
20613
20614 if (0 < n.children.length && !n.isLocked) {
20615 n.positionX = (n.maxX + n.minX) / 2;
20616 n.positionY = (n.maxY + n.minY) / 2;
20617 n.width = n.maxX - n.minX;
20618 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
20619 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20620 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20621 // logDebug(s);
20622 }
20623 }
20624};
20625/**
20626 * @brief : Limits a force (forceX, forceY) to be not
20627 * greater (in modulo) than max.
20628 8 Preserves force direction.
20629 */
20630
20631
20632var limitForce = function limitForce(forceX, forceY, max) {
20633 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20634 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20635
20636 if (force > max) {
20637 var res = {
20638 x: max * forceX / force,
20639 y: max * forceY / force
20640 };
20641 } else {
20642 var res = {
20643 x: forceX,
20644 y: forceY
20645 };
20646 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20647 // logDebug(s);
20648
20649
20650 return res;
20651};
20652/**
20653 * @brief : Function used for keeping track of compound node
20654 * sizes, since they should bound all their subnodes.
20655 */
20656
20657
20658var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20659 // var s = "Propagating new position/size of node " + node.id;
20660 var parentId = node.parentId;
20661
20662 if (null == parentId) {
20663 // If there's no parent, we are done
20664 // s += ". No parent node.";
20665 // logDebug(s);
20666 return;
20667 } // Get Parent Node
20668
20669
20670 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20671 var flag = false; // MaxX
20672
20673 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20674 p.maxX = node.maxX + p.padRight;
20675 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20676 } // MinX
20677
20678
20679 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20680 p.minX = node.minX - p.padLeft;
20681 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20682 } // MaxY
20683
20684
20685 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20686 p.maxY = node.maxY + p.padBottom;
20687 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20688 } // MinY
20689
20690
20691 if (null == p.minY || node.minY - p.padTop < p.minY) {
20692 p.minY = node.minY - p.padTop;
20693 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20694 } // If updated boundaries, propagate changes upward
20695
20696
20697 if (flag) {
20698 // logDebug(s);
20699 return updateAncestryBoundaries(p, layoutInfo);
20700 } // s += ". No changes in boundaries/position of parent node " + p.id;
20701 // logDebug(s);
20702
20703
20704 return;
20705};
20706
20707var separateComponents = function separateComponents(layoutInfo, options) {
20708 var nodes = layoutInfo.layoutNodes;
20709 var components = [];
20710
20711 for (var i = 0; i < nodes.length; i++) {
20712 var node = nodes[i];
20713 var cid = node.cmptId;
20714 var component = components[cid] = components[cid] || [];
20715 component.push(node);
20716 }
20717
20718 var totalA = 0;
20719
20720 for (var i = 0; i < components.length; i++) {
20721 var c = components[i];
20722
20723 if (!c) {
20724 continue;
20725 }
20726
20727 c.x1 = Infinity;
20728 c.x2 = -Infinity;
20729 c.y1 = Infinity;
20730 c.y2 = -Infinity;
20731
20732 for (var j = 0; j < c.length; j++) {
20733 var n = c[j];
20734 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
20735 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
20736 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
20737 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
20738 }
20739
20740 c.w = c.x2 - c.x1;
20741 c.h = c.y2 - c.y1;
20742 totalA += c.w * c.h;
20743 }
20744
20745 components.sort(function (c1, c2) {
20746 return c2.w * c2.h - c1.w * c1.h;
20747 });
20748 var x = 0;
20749 var y = 0;
20750 var usedW = 0;
20751 var rowH = 0;
20752 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
20753
20754 for (var i = 0; i < components.length; i++) {
20755 var c = components[i];
20756
20757 if (!c) {
20758 continue;
20759 }
20760
20761 for (var j = 0; j < c.length; j++) {
20762 var n = c[j];
20763
20764 if (!n.isLocked) {
20765 n.positionX += x - c.x1;
20766 n.positionY += y - c.y1;
20767 }
20768 }
20769
20770 x += c.w + options.componentSpacing;
20771 usedW += c.w + options.componentSpacing;
20772 rowH = Math.max(rowH, c.h);
20773
20774 if (usedW > maxRowW) {
20775 y += rowH + options.componentSpacing;
20776 x = 0;
20777 usedW = 0;
20778 rowH = 0;
20779 }
20780 }
20781};
20782
20783var defaults$d = {
20784 fit: true,
20785 // whether to fit the viewport to the graph
20786 padding: 30,
20787 // padding used on fit
20788 boundingBox: undefined,
20789 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20790 avoidOverlap: true,
20791 // prevents node overlap, may overflow boundingBox if not enough space
20792 avoidOverlapPadding: 10,
20793 // extra spacing around nodes when avoidOverlap: true
20794 nodeDimensionsIncludeLabels: false,
20795 // Excludes the label when calculating node bounding boxes for the layout algorithm
20796 spacingFactor: undefined,
20797 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20798 condense: false,
20799 // uses all available space on false, uses minimal space on true
20800 rows: undefined,
20801 // force num of rows in the grid
20802 cols: undefined,
20803 // force num of columns in the grid
20804 position: function position(node) {},
20805 // returns { row, col } for element
20806 sort: undefined,
20807 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20808 animate: false,
20809 // whether to transition the node positions
20810 animationDuration: 500,
20811 // duration of animation in ms if enabled
20812 animationEasing: undefined,
20813 // easing of animation if enabled
20814 animateFilter: function animateFilter(node, i) {
20815 return true;
20816 },
20817 // 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
20818 ready: undefined,
20819 // callback on layoutready
20820 stop: undefined,
20821 // callback on layoutstop
20822 transform: function transform(node, position) {
20823 return position;
20824 } // transform a given node position. Useful for changing flow direction in discrete layouts
20825
20826};
20827
20828function GridLayout(options) {
20829 this.options = extend({}, defaults$d, options);
20830}
20831
20832GridLayout.prototype.run = function () {
20833 var params = this.options;
20834 var options = params;
20835 var cy = params.cy;
20836 var eles = options.eles;
20837 var nodes = eles.nodes().not(':parent');
20838
20839 if (options.sort) {
20840 nodes = nodes.sort(options.sort);
20841 }
20842
20843 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20844 x1: 0,
20845 y1: 0,
20846 w: cy.width(),
20847 h: cy.height()
20848 });
20849
20850 if (bb.h === 0 || bb.w === 0) {
20851 nodes.layoutPositions(this, options, function (ele) {
20852 return {
20853 x: bb.x1,
20854 y: bb.y1
20855 };
20856 });
20857 } else {
20858 // width/height * splits^2 = cells where splits is number of times to split width
20859 var cells = nodes.size();
20860 var splits = Math.sqrt(cells * bb.h / bb.w);
20861 var rows = Math.round(splits);
20862 var cols = Math.round(bb.w / bb.h * splits);
20863
20864 var small = function small(val) {
20865 if (val == null) {
20866 return Math.min(rows, cols);
20867 } else {
20868 var min = Math.min(rows, cols);
20869
20870 if (min == rows) {
20871 rows = val;
20872 } else {
20873 cols = val;
20874 }
20875 }
20876 };
20877
20878 var large = function large(val) {
20879 if (val == null) {
20880 return Math.max(rows, cols);
20881 } else {
20882 var max = Math.max(rows, cols);
20883
20884 if (max == rows) {
20885 rows = val;
20886 } else {
20887 cols = val;
20888 }
20889 }
20890 };
20891
20892 var oRows = options.rows;
20893 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
20894
20895 if (oRows != null && oCols != null) {
20896 rows = oRows;
20897 cols = oCols;
20898 } else if (oRows != null && oCols == null) {
20899 rows = oRows;
20900 cols = Math.ceil(cells / rows);
20901 } else if (oRows == null && oCols != null) {
20902 cols = oCols;
20903 rows = Math.ceil(cells / cols);
20904 } // otherwise use the automatic values and adjust accordingly
20905 // if rounding was up, see if we can reduce rows or columns
20906 else if (cols * rows > cells) {
20907 var sm = small();
20908 var lg = large(); // reducing the small side takes away the most cells, so try it first
20909
20910 if ((sm - 1) * lg >= cells) {
20911 small(sm - 1);
20912 } else if ((lg - 1) * sm >= cells) {
20913 large(lg - 1);
20914 }
20915 } else {
20916 // if rounding was too low, add rows or columns
20917 while (cols * rows < cells) {
20918 var _sm = small();
20919
20920 var _lg = large(); // try to add to larger side first (adds less in multiplication)
20921
20922
20923 if ((_lg + 1) * _sm >= cells) {
20924 large(_lg + 1);
20925 } else {
20926 small(_sm + 1);
20927 }
20928 }
20929 }
20930
20931 var cellWidth = bb.w / cols;
20932 var cellHeight = bb.h / rows;
20933
20934 if (options.condense) {
20935 cellWidth = 0;
20936 cellHeight = 0;
20937 }
20938
20939 if (options.avoidOverlap) {
20940 for (var i = 0; i < nodes.length; i++) {
20941 var node = nodes[i];
20942 var pos = node._private.position;
20943
20944 if (pos.x == null || pos.y == null) {
20945 // for bb
20946 pos.x = 0;
20947 pos.y = 0;
20948 }
20949
20950 var nbb = node.layoutDimensions(options);
20951 var p = options.avoidOverlapPadding;
20952 var w = nbb.w + p;
20953 var h = nbb.h + p;
20954 cellWidth = Math.max(cellWidth, w);
20955 cellHeight = Math.max(cellHeight, h);
20956 }
20957 }
20958
20959 var cellUsed = {}; // e.g. 'c-0-2' => true
20960
20961 var used = function used(row, col) {
20962 return cellUsed['c-' + row + '-' + col] ? true : false;
20963 };
20964
20965 var use = function use(row, col) {
20966 cellUsed['c-' + row + '-' + col] = true;
20967 }; // to keep track of current cell position
20968
20969
20970 var row = 0;
20971 var col = 0;
20972
20973 var moveToNextCell = function moveToNextCell() {
20974 col++;
20975
20976 if (col >= cols) {
20977 col = 0;
20978 row++;
20979 }
20980 }; // get a cache of all the manual positions
20981
20982
20983 var id2manPos = {};
20984
20985 for (var _i = 0; _i < nodes.length; _i++) {
20986 var _node = nodes[_i];
20987 var rcPos = options.position(_node);
20988
20989 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
20990 // must have at least row or col def'd
20991 var _pos = {
20992 row: rcPos.row,
20993 col: rcPos.col
20994 };
20995
20996 if (_pos.col === undefined) {
20997 // find unused col
20998 _pos.col = 0;
20999
21000 while (used(_pos.row, _pos.col)) {
21001 _pos.col++;
21002 }
21003 } else if (_pos.row === undefined) {
21004 // find unused row
21005 _pos.row = 0;
21006
21007 while (used(_pos.row, _pos.col)) {
21008 _pos.row++;
21009 }
21010 }
21011
21012 id2manPos[_node.id()] = _pos;
21013 use(_pos.row, _pos.col);
21014 }
21015 }
21016
21017 var getPos = function getPos(element, i) {
21018 var x, y;
21019
21020 if (element.locked() || element.isParent()) {
21021 return false;
21022 } // see if we have a manual position set
21023
21024
21025 var rcPos = id2manPos[element.id()];
21026
21027 if (rcPos) {
21028 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21029 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21030 } else {
21031 // otherwise set automatically
21032 while (used(row, col)) {
21033 moveToNextCell();
21034 }
21035
21036 x = col * cellWidth + cellWidth / 2 + bb.x1;
21037 y = row * cellHeight + cellHeight / 2 + bb.y1;
21038 use(row, col);
21039 moveToNextCell();
21040 }
21041
21042 return {
21043 x: x,
21044 y: y
21045 };
21046 };
21047
21048 nodes.layoutPositions(this, options, getPos);
21049 }
21050
21051 return this; // chaining
21052};
21053
21054var defaults$e = {
21055 ready: function ready() {},
21056 // on layoutready
21057 stop: function stop() {} // on layoutstop
21058
21059}; // constructor
21060// options : object containing layout options
21061
21062function NullLayout(options) {
21063 this.options = extend({}, defaults$e, options);
21064} // runs the layout
21065
21066
21067NullLayout.prototype.run = function () {
21068 var options = this.options;
21069 var eles = options.eles; // elements to consider in the layout
21070
21071 var layout = this; // cy is automatically populated for us in the constructor
21072 // (disable eslint for next line as this serves as example layout code to external developers)
21073 // eslint-disable-next-line no-unused-vars
21074
21075 var cy = options.cy;
21076 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21077 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21078
21079 eles.nodes().positions(function () {
21080 return {
21081 x: 0,
21082 y: 0
21083 };
21084 }); // trigger layoutready when each node has had its position set at least once
21085
21086 layout.one('layoutready', options.ready);
21087 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21088
21089 layout.one('layoutstop', options.stop);
21090 layout.emit('layoutstop');
21091 return this; // chaining
21092}; // called on continuous layouts to stop them before they finish
21093
21094
21095NullLayout.prototype.stop = function () {
21096 return this; // chaining
21097};
21098
21099var defaults$f = {
21100 positions: undefined,
21101 // map of (node id) => (position obj); or function(node){ return somPos; }
21102 zoom: undefined,
21103 // the zoom level to set (prob want fit = false if set)
21104 pan: undefined,
21105 // the pan level to set (prob want fit = false if set)
21106 fit: true,
21107 // whether to fit to viewport
21108 padding: 30,
21109 // padding on fit
21110 animate: false,
21111 // whether to transition the node positions
21112 animationDuration: 500,
21113 // duration of animation in ms if enabled
21114 animationEasing: undefined,
21115 // easing of animation if enabled
21116 animateFilter: function animateFilter(node, i) {
21117 return true;
21118 },
21119 // 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
21120 ready: undefined,
21121 // callback on layoutready
21122 stop: undefined,
21123 // callback on layoutstop
21124 transform: function transform(node, position) {
21125 return position;
21126 } // transform a given node position. Useful for changing flow direction in discrete layouts
21127
21128};
21129
21130function PresetLayout(options) {
21131 this.options = extend({}, defaults$f, options);
21132}
21133
21134PresetLayout.prototype.run = function () {
21135 var options = this.options;
21136 var eles = options.eles;
21137 var nodes = eles.nodes();
21138 var posIsFn = fn(options.positions);
21139
21140 function getPosition(node) {
21141 if (options.positions == null) {
21142 return copyPosition(node.position());
21143 }
21144
21145 if (posIsFn) {
21146 return options.positions(node);
21147 }
21148
21149 var pos = options.positions[node._private.data.id];
21150
21151 if (pos == null) {
21152 return null;
21153 }
21154
21155 return pos;
21156 }
21157
21158 nodes.layoutPositions(this, options, function (node, i) {
21159 var position = getPosition(node);
21160
21161 if (node.locked() || position == null) {
21162 return false;
21163 }
21164
21165 return position;
21166 });
21167 return this; // chaining
21168};
21169
21170var defaults$g = {
21171 fit: true,
21172 // whether to fit to viewport
21173 padding: 30,
21174 // fit padding
21175 boundingBox: undefined,
21176 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21177 animate: false,
21178 // whether to transition the node positions
21179 animationDuration: 500,
21180 // duration of animation in ms if enabled
21181 animationEasing: undefined,
21182 // easing of animation if enabled
21183 animateFilter: function animateFilter(node, i) {
21184 return true;
21185 },
21186 // 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
21187 ready: undefined,
21188 // callback on layoutready
21189 stop: undefined,
21190 // callback on layoutstop
21191 transform: function transform(node, position) {
21192 return position;
21193 } // transform a given node position. Useful for changing flow direction in discrete layouts
21194
21195};
21196
21197function RandomLayout(options) {
21198 this.options = extend({}, defaults$g, options);
21199}
21200
21201RandomLayout.prototype.run = function () {
21202 var options = this.options;
21203 var cy = options.cy;
21204 var eles = options.eles;
21205 var nodes = eles.nodes().not(':parent');
21206 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21207 x1: 0,
21208 y1: 0,
21209 w: cy.width(),
21210 h: cy.height()
21211 });
21212
21213 var getPos = function getPos(node, i) {
21214 return {
21215 x: bb.x1 + Math.round(Math.random() * bb.w),
21216 y: bb.y1 + Math.round(Math.random() * bb.h)
21217 };
21218 };
21219
21220 nodes.layoutPositions(this, options, getPos);
21221 return this; // chaining
21222};
21223
21224var layout = [{
21225 name: 'breadthfirst',
21226 impl: BreadthFirstLayout
21227}, {
21228 name: 'circle',
21229 impl: CircleLayout
21230}, {
21231 name: 'concentric',
21232 impl: ConcentricLayout
21233}, {
21234 name: 'cose',
21235 impl: CoseLayout
21236}, {
21237 name: 'grid',
21238 impl: GridLayout
21239}, {
21240 name: 'null',
21241 impl: NullLayout
21242}, {
21243 name: 'preset',
21244 impl: PresetLayout
21245}, {
21246 name: 'random',
21247 impl: RandomLayout
21248}];
21249
21250function NullRenderer(options) {
21251 this.options = options;
21252 this.notifications = 0; // for testing
21253}
21254
21255var noop$1 = function noop() {};
21256
21257var throwImgErr = function throwImgErr() {
21258 throw new Error('A headless instance can not render images');
21259};
21260
21261NullRenderer.prototype = {
21262 recalculateRenderedStyle: noop$1,
21263 notify: function notify() {
21264 this.notifications++;
21265 },
21266 init: noop$1,
21267 isHeadless: function isHeadless() {
21268 return true;
21269 },
21270 png: throwImgErr,
21271 jpg: throwImgErr
21272};
21273
21274var BRp = {};
21275BRp.arrowShapeWidth = 0.3;
21276
21277BRp.registerArrowShapes = function () {
21278 var arrowShapes = this.arrowShapes = {};
21279 var renderer = this; // Contract for arrow shapes:
21280 // 0, 0 is arrow tip
21281 // (0, 1) is direction towards node
21282 // (1, 0) is right
21283 //
21284 // functional api:
21285 // collide: check x, y in shape
21286 // roughCollide: called before collide, no false negatives
21287 // draw: draw
21288 // spacing: dist(arrowTip, nodeBoundary)
21289 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21290
21291 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21292 var x1 = translation.x - size / 2 - padding;
21293 var x2 = translation.x + size / 2 + padding;
21294 var y1 = translation.y - size / 2 - padding;
21295 var y2 = translation.y + size / 2 + padding;
21296 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21297 return inside;
21298 };
21299
21300 var transform = function transform(x, y, size, angle, translation) {
21301 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21302 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21303 var xScaled = xRotated * size;
21304 var yScaled = yRotated * size;
21305 var xTranslated = xScaled + translation.x;
21306 var yTranslated = yScaled + translation.y;
21307 return {
21308 x: xTranslated,
21309 y: yTranslated
21310 };
21311 };
21312
21313 var transformPoints = function transformPoints(pts, size, angle, translation) {
21314 var retPts = [];
21315
21316 for (var i = 0; i < pts.length; i += 2) {
21317 var x = pts[i];
21318 var y = pts[i + 1];
21319 retPts.push(transform(x, y, size, angle, translation));
21320 }
21321
21322 return retPts;
21323 };
21324
21325 var pointsToArr = function pointsToArr(pts) {
21326 var ret = [];
21327
21328 for (var i = 0; i < pts.length; i++) {
21329 var p = pts[i];
21330 ret.push(p.x, p.y);
21331 }
21332
21333 return ret;
21334 };
21335
21336 var standardGap = function standardGap(edge) {
21337 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21338 };
21339
21340 var defineArrowShape = function defineArrowShape(name, defn) {
21341 if (string(defn)) {
21342 defn = arrowShapes[defn];
21343 }
21344
21345 arrowShapes[name] = extend({
21346 name: name,
21347 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21348 collide: function collide(x, y, size, angle, translation, padding) {
21349 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21350 var inside = pointInsidePolygonPoints(x, y, points);
21351 return inside;
21352 },
21353 roughCollide: bbCollide,
21354 draw: function draw(context, size, angle, translation) {
21355 var points = transformPoints(this.points, size, angle, translation);
21356 renderer.arrowShapeImpl('polygon')(context, points);
21357 },
21358 spacing: function spacing(edge) {
21359 return 0;
21360 },
21361 gap: standardGap
21362 }, defn);
21363 };
21364
21365 defineArrowShape('none', {
21366 collide: falsify,
21367 roughCollide: falsify,
21368 draw: noop,
21369 spacing: zeroify,
21370 gap: zeroify
21371 });
21372 defineArrowShape('triangle', {
21373 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21374 });
21375 defineArrowShape('arrow', 'triangle');
21376 defineArrowShape('triangle-backcurve', {
21377 points: arrowShapes['triangle'].points,
21378 controlPoint: [0, -0.15],
21379 roughCollide: bbCollide,
21380 draw: function draw(context, size, angle, translation, edgeWidth) {
21381 var ptsTrans = transformPoints(this.points, size, angle, translation);
21382 var ctrlPt = this.controlPoint;
21383 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21384 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21385 },
21386 gap: function gap(edge) {
21387 return standardGap(edge) * 0.8;
21388 }
21389 });
21390 defineArrowShape('triangle-tee', {
21391 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21392 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21393 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21394 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21395 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21396 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21397 return inside;
21398 },
21399 draw: function draw(context, size, angle, translation, edgeWidth) {
21400 var triPts = transformPoints(this.points, size, angle, translation);
21401 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21402 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21403 }
21404 });
21405 defineArrowShape('triangle-cross', {
21406 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21407 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
21408 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
21409 0.15, -0.4],
21410 crossLinePts: function crossLinePts(size, edgeWidth) {
21411 // shift points so that the distance between the cross points matches edge width
21412 var p = this.baseCrossLinePts.slice();
21413 var shiftFactor = edgeWidth / size;
21414 var y0 = 3;
21415 var y1 = 5;
21416 p[y0] = p[y0] - shiftFactor;
21417 p[y1] = p[y1] - shiftFactor;
21418 return p;
21419 },
21420 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21421 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21422 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21423 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21424 return inside;
21425 },
21426 draw: function draw(context, size, angle, translation, edgeWidth) {
21427 var triPts = transformPoints(this.points, size, angle, translation);
21428 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21429 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21430 }
21431 });
21432 defineArrowShape('vee', {
21433 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21434 gap: function gap(edge) {
21435 return standardGap(edge) * 0.525;
21436 }
21437 });
21438 defineArrowShape('circle', {
21439 radius: 0.15,
21440 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21441 var t = translation;
21442 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21443 return inside;
21444 },
21445 draw: function draw(context, size, angle, translation, edgeWidth) {
21446 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21447 },
21448 spacing: function spacing(edge) {
21449 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21450 }
21451 });
21452 defineArrowShape('tee', {
21453 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21454 spacing: function spacing(edge) {
21455 return 1;
21456 },
21457 gap: function gap(edge) {
21458 return 1;
21459 }
21460 });
21461 defineArrowShape('square', {
21462 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21463 });
21464 defineArrowShape('diamond', {
21465 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21466 gap: function gap(edge) {
21467 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21468 }
21469 });
21470 defineArrowShape('chevron', {
21471 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21472 gap: function gap(edge) {
21473 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21474 }
21475 });
21476};
21477
21478var BRp$1 = {}; // Project mouse
21479
21480BRp$1.projectIntoViewport = function (clientX, clientY) {
21481 var cy = this.cy;
21482 var offsets = this.findContainerClientCoords();
21483 var offsetLeft = offsets[0];
21484 var offsetTop = offsets[1];
21485 var scale = offsets[4];
21486 var pan = cy.pan();
21487 var zoom = cy.zoom();
21488 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21489 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21490 return [x, y];
21491};
21492
21493BRp$1.findContainerClientCoords = function () {
21494 if (this.containerBB) {
21495 return this.containerBB;
21496 }
21497
21498 var container = this.container;
21499 var rect = container.getBoundingClientRect();
21500 var style = window$1.getComputedStyle(container);
21501
21502 var styleValue = function styleValue(name) {
21503 return parseFloat(style.getPropertyValue(name));
21504 };
21505
21506 var padding = {
21507 left: styleValue('padding-left'),
21508 right: styleValue('padding-right'),
21509 top: styleValue('padding-top'),
21510 bottom: styleValue('padding-bottom')
21511 };
21512 var border = {
21513 left: styleValue('border-left-width'),
21514 right: styleValue('border-right-width'),
21515 top: styleValue('border-top-width'),
21516 bottom: styleValue('border-bottom-width')
21517 };
21518 var clientWidth = container.clientWidth;
21519 var clientHeight = container.clientHeight;
21520 var paddingHor = padding.left + padding.right;
21521 var paddingVer = padding.top + padding.bottom;
21522 var borderHor = border.left + border.right;
21523 var scale = rect.width / (clientWidth + borderHor);
21524 var unscaledW = clientWidth - paddingHor;
21525 var unscaledH = clientHeight - paddingVer;
21526 var left = rect.left + padding.left + border.left;
21527 var top = rect.top + padding.top + border.top;
21528 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21529};
21530
21531BRp$1.invalidateContainerClientCoordsCache = function () {
21532 this.containerBB = null;
21533};
21534
21535BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21536 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21537};
21538
21539BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21540 var self = this;
21541 var r = this;
21542 var eles = r.getCachedZSortedEles();
21543 var near = []; // 1 node max, 1 edge max
21544
21545 var zoom = r.cy.zoom();
21546 var hasCompounds = r.cy.hasCompoundNodes();
21547 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21548 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21549 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21550 var minSqDist = Infinity;
21551 var nearEdge;
21552 var nearNode;
21553
21554 if (interactiveElementsOnly) {
21555 eles = eles.interactive;
21556 }
21557
21558 function addEle(ele, sqDist) {
21559 if (ele.isNode()) {
21560 if (nearNode) {
21561 return; // can't replace node
21562 } else {
21563 nearNode = ele;
21564 near.push(ele);
21565 }
21566 }
21567
21568 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21569 if (nearEdge) {
21570 // then replace existing edge
21571 // can replace only if same z-index
21572 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) {
21573 for (var i = 0; i < near.length; i++) {
21574 if (near[i].isEdge()) {
21575 near[i] = ele;
21576 nearEdge = ele;
21577 minSqDist = sqDist != null ? sqDist : minSqDist;
21578 break;
21579 }
21580 }
21581 }
21582 } else {
21583 near.push(ele);
21584 nearEdge = ele;
21585 minSqDist = sqDist != null ? sqDist : minSqDist;
21586 }
21587 }
21588 }
21589
21590 function checkNode(node) {
21591 var width = node.outerWidth() + 2 * nodeThreshold;
21592 var height = node.outerHeight() + 2 * nodeThreshold;
21593 var hw = width / 2;
21594 var hh = height / 2;
21595 var pos = node.position();
21596
21597 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21598 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21599 ) {
21600 var shape = r.nodeShapes[self.getNodeShape(node)];
21601
21602 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
21603 addEle(node, 0);
21604 return true;
21605 }
21606 }
21607 }
21608
21609 function checkEdge(edge) {
21610 var _p = edge._private;
21611 var rs = _p.rscratch;
21612 var styleWidth = edge.pstyle('width').pfValue;
21613 var scale = edge.pstyle('arrow-scale').value;
21614 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21615
21616 var widthSq = width * width;
21617 var width2 = width * 2;
21618 var src = _p.source;
21619 var tgt = _p.target;
21620 var sqDist;
21621
21622 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21623 var pts = rs.allpts;
21624
21625 for (var i = 0; i + 3 < pts.length; i += 2) {
21626 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]))) {
21627 addEle(edge, sqDist);
21628 return true;
21629 }
21630 }
21631 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21632 var pts = rs.allpts;
21633
21634 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21635 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]))) {
21636 addEle(edge, sqDist);
21637 return true;
21638 }
21639 }
21640 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
21641
21642
21643 var src = src || _p.source;
21644 var tgt = tgt || _p.target;
21645 var arSize = self.getArrowWidth(styleWidth, scale);
21646 var arrows = [{
21647 name: 'source',
21648 x: rs.arrowStartX,
21649 y: rs.arrowStartY,
21650 angle: rs.srcArrowAngle
21651 }, {
21652 name: 'target',
21653 x: rs.arrowEndX,
21654 y: rs.arrowEndY,
21655 angle: rs.tgtArrowAngle
21656 }, {
21657 name: 'mid-source',
21658 x: rs.midX,
21659 y: rs.midY,
21660 angle: rs.midsrcArrowAngle
21661 }, {
21662 name: 'mid-target',
21663 x: rs.midX,
21664 y: rs.midY,
21665 angle: rs.midtgtArrowAngle
21666 }];
21667
21668 for (var i = 0; i < arrows.length; i++) {
21669 var ar = arrows[i];
21670 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21671 var edgeWidth = edge.pstyle('width').pfValue;
21672
21673 if (shape.roughCollide(x, y, arSize, ar.angle, {
21674 x: ar.x,
21675 y: ar.y
21676 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21677 x: ar.x,
21678 y: ar.y
21679 }, edgeWidth, edgeThreshold)) {
21680 addEle(edge);
21681 return true;
21682 }
21683 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21684
21685
21686 if (hasCompounds && near.length > 0) {
21687 checkNode(src);
21688 checkNode(tgt);
21689 }
21690 }
21691
21692 function preprop(obj, name, pre) {
21693 return getPrefixedProperty(obj, name, pre);
21694 }
21695
21696 function checkLabel(ele, prefix) {
21697 var _p = ele._private;
21698 var th = labelThreshold;
21699 var prefixDash;
21700
21701 if (prefix) {
21702 prefixDash = prefix + '-';
21703 } else {
21704 prefixDash = '';
21705 }
21706
21707 ele.boundingBox();
21708 var bb = _p.labelBounds[prefix || 'main'];
21709 var text = ele.pstyle(prefixDash + 'label').value;
21710 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21711
21712 if (!eventsEnabled || !text) {
21713 return;
21714 }
21715
21716 var rstyle = _p.rstyle;
21717 var lx = preprop(rstyle, 'labelX', prefix);
21718 var ly = preprop(rstyle, 'labelY', prefix);
21719 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21720 var lx1 = bb.x1 - th;
21721 var lx2 = bb.x2 + th;
21722 var ly1 = bb.y1 - th;
21723 var ly2 = bb.y2 + th;
21724
21725 if (theta) {
21726 var cos = Math.cos(theta);
21727 var sin = Math.sin(theta);
21728
21729 var rotate = function rotate(x, y) {
21730 x = x - lx;
21731 y = y - ly;
21732 return {
21733 x: x * cos - y * sin + lx,
21734 y: x * sin + y * cos + ly
21735 };
21736 };
21737
21738 var px1y1 = rotate(lx1, ly1);
21739 var px1y2 = rotate(lx1, ly2);
21740 var px2y1 = rotate(lx2, ly1);
21741 var px2y2 = rotate(lx2, ly2);
21742 var points = [px1y1.x, px1y1.y, px2y1.x, px2y1.y, px2y2.x, px2y2.y, px1y2.x, px1y2.y];
21743
21744 if (pointInsidePolygonPoints(x, y, points)) {
21745 addEle(ele);
21746 return true;
21747 }
21748 } else {
21749 // do a cheaper bb check
21750 if (inBoundingBox(bb, x, y)) {
21751 addEle(ele);
21752 return true;
21753 }
21754 }
21755 }
21756
21757 for (var i = eles.length - 1; i >= 0; i--) {
21758 // reverse order for precedence
21759 var ele = eles[i];
21760
21761 if (ele.isNode()) {
21762 checkNode(ele) || checkLabel(ele);
21763 } else {
21764 // then edge
21765 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
21766 }
21767 }
21768
21769 return near;
21770}; // 'Give me everything from this box'
21771
21772
21773BRp$1.getAllInBox = function (x1, y1, x2, y2) {
21774 var eles = this.getCachedZSortedEles().interactive;
21775 var box = [];
21776 var x1c = Math.min(x1, x2);
21777 var x2c = Math.max(x1, x2);
21778 var y1c = Math.min(y1, y2);
21779 var y2c = Math.max(y1, y2);
21780 x1 = x1c;
21781 x2 = x2c;
21782 y1 = y1c;
21783 y2 = y2c;
21784 var boxBb = makeBoundingBox({
21785 x1: x1,
21786 y1: y1,
21787 x2: x2,
21788 y2: y2
21789 });
21790
21791 for (var e = 0; e < eles.length; e++) {
21792 var ele = eles[e];
21793
21794 if (ele.isNode()) {
21795 var node = ele;
21796 var nodeBb = node.boundingBox({
21797 includeNodes: true,
21798 includeEdges: false,
21799 includeLabels: false
21800 });
21801
21802 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
21803 box.push(node);
21804 }
21805 } else {
21806 var edge = ele;
21807 var _p = edge._private;
21808 var rs = _p.rscratch;
21809
21810 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
21811 continue;
21812 }
21813
21814 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
21815 continue;
21816 }
21817
21818 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
21819 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
21820 var allInside = true;
21821
21822 for (var i = 0; i < pts.length; i++) {
21823 if (!pointInBoundingBox(boxBb, pts[i])) {
21824 allInside = false;
21825 break;
21826 }
21827 }
21828
21829 if (allInside) {
21830 box.push(edge);
21831 }
21832 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
21833 box.push(edge);
21834 }
21835 }
21836 }
21837
21838 return box;
21839};
21840
21841var BRp$2 = {};
21842
21843BRp$2.calculateArrowAngles = function (edge) {
21844 var rs = edge._private.rscratch;
21845 var isHaystack = rs.edgeType === 'haystack';
21846 var isBezier = rs.edgeType === 'bezier';
21847 var isMultibezier = rs.edgeType === 'multibezier';
21848 var isSegments = rs.edgeType === 'segments';
21849 var isCompound = rs.edgeType === 'compound';
21850 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
21851
21852 var dispX, dispY;
21853 var startX, startY, endX, endY, midX, midY;
21854
21855 if (isHaystack) {
21856 startX = rs.haystackPts[0];
21857 startY = rs.haystackPts[1];
21858 endX = rs.haystackPts[2];
21859 endY = rs.haystackPts[3];
21860 } else {
21861 startX = rs.arrowStartX;
21862 startY = rs.arrowStartY;
21863 endX = rs.arrowEndX;
21864 endY = rs.arrowEndY;
21865 }
21866
21867 midX = rs.midX;
21868 midY = rs.midY; // source
21869 //
21870
21871 if (isSegments) {
21872 dispX = startX - rs.segpts[0];
21873 dispY = startY - rs.segpts[1];
21874 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21875 var pts = rs.allpts;
21876 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
21877 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
21878 dispX = startX - bX;
21879 dispY = startY - bY;
21880 } else {
21881 dispX = startX - midX;
21882 dispY = startY - midY;
21883 }
21884
21885 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
21886 //
21887
21888 var midX = rs.midX;
21889 var midY = rs.midY;
21890
21891 if (isHaystack) {
21892 midX = (startX + endX) / 2;
21893 midY = (startY + endY) / 2;
21894 }
21895
21896 dispX = endX - startX;
21897 dispY = endY - startY;
21898
21899 if (isSegments) {
21900 var pts = rs.allpts;
21901
21902 if (pts.length / 2 % 2 === 0) {
21903 var i2 = pts.length / 2;
21904 var i1 = i2 - 2;
21905 dispX = pts[i2] - pts[i1];
21906 dispY = pts[i2 + 1] - pts[i1 + 1];
21907 } else {
21908 var i2 = pts.length / 2 - 1;
21909 var i1 = i2 - 2;
21910 var i3 = i2 + 2;
21911 dispX = pts[i2] - pts[i1];
21912 dispY = pts[i2 + 1] - pts[i1 + 1];
21913 }
21914 } else if (isMultibezier || isCompound || isSelf) {
21915 var pts = rs.allpts;
21916 var cpts = rs.ctrlpts;
21917 var bp0x, bp0y;
21918 var bp1x, bp1y;
21919
21920 if (cpts.length / 2 % 2 === 0) {
21921 var p0 = pts.length / 2 - 1; // startpt
21922
21923 var ic = p0 + 2;
21924 var p1 = ic + 2;
21925 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
21926 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
21927 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
21928 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
21929 } else {
21930 var ic = pts.length / 2 - 1; // ctrpt
21931
21932 var p0 = ic - 2; // startpt
21933
21934 var p1 = ic + 2; // endpt
21935
21936 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
21937 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
21938 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
21939 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
21940 }
21941
21942 dispX = bp1x - bp0x;
21943 dispY = bp1y - bp0y;
21944 }
21945
21946 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
21947 rs.midDispX = dispX;
21948 rs.midDispY = dispY; // mid source
21949 //
21950
21951 dispX *= -1;
21952 dispY *= -1;
21953
21954 if (isSegments) {
21955 var pts = rs.allpts;
21956
21957 if (pts.length / 2 % 2 === 0) ; else {
21958 var i2 = pts.length / 2 - 1;
21959 var i3 = i2 + 2;
21960 dispX = -(pts[i3] - pts[i2]);
21961 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
21962 }
21963 }
21964
21965 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
21966 //
21967
21968 if (isSegments) {
21969 dispX = endX - rs.segpts[rs.segpts.length - 2];
21970 dispY = endY - rs.segpts[rs.segpts.length - 1];
21971 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21972 var pts = rs.allpts;
21973 var l = pts.length;
21974 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
21975 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
21976 dispX = endX - bX;
21977 dispY = endY - bY;
21978 } else {
21979 dispX = endX - midX;
21980 dispY = endY - midY;
21981 }
21982
21983 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
21984};
21985
21986BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
21987 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
21988 var cachedVal = cache[edgeWidth + ', ' + scale];
21989
21990 if (cachedVal) {
21991 return cachedVal;
21992 }
21993
21994 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
21995 cache[edgeWidth + ', ' + scale] = cachedVal;
21996 return cachedVal;
21997};
21998
21999var BRp$3 = {};
22000
22001BRp$3.findHaystackPoints = function (edges) {
22002 for (var i = 0; i < edges.length; i++) {
22003 var edge = edges[i];
22004 var _p = edge._private;
22005 var rs = _p.rscratch;
22006
22007 if (!rs.haystack) {
22008 var angle = Math.random() * 2 * Math.PI;
22009 rs.source = {
22010 x: Math.cos(angle),
22011 y: Math.sin(angle)
22012 };
22013 angle = Math.random() * 2 * Math.PI;
22014 rs.target = {
22015 x: Math.cos(angle),
22016 y: Math.sin(angle)
22017 };
22018 }
22019
22020 var src = _p.source;
22021 var tgt = _p.target;
22022 var srcPos = src.position();
22023 var tgtPos = tgt.position();
22024 var srcW = src.width();
22025 var tgtW = tgt.width();
22026 var srcH = src.height();
22027 var tgtH = tgt.height();
22028 var radius = edge.pstyle('haystack-radius').value;
22029 var halfRadius = radius / 2; // b/c have to half width/height
22030
22031 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];
22032 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22033 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22034
22035 rs.edgeType = 'haystack';
22036 rs.haystack = true;
22037 this.storeEdgeProjections(edge);
22038 this.calculateArrowAngles(edge);
22039 this.recalculateEdgeLabelProjections(edge);
22040 this.calculateLabelAngles(edge);
22041 }
22042};
22043
22044BRp$3.findSegmentsPoints = function (edge, pairInfo) {
22045 // Segments (multiple straight lines)
22046 var rs = edge._private.rscratch;
22047 var posPts = pairInfo.posPts,
22048 intersectionPts = pairInfo.intersectionPts,
22049 vectorNormInverse = pairInfo.vectorNormInverse;
22050 var edgeDistances = edge.pstyle('edge-distances').value;
22051 var segmentWs = edge.pstyle('segment-weights');
22052 var segmentDs = edge.pstyle('segment-distances');
22053 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22054 rs.edgeType = 'segments';
22055 rs.segpts = [];
22056
22057 for (var s = 0; s < segmentsN; s++) {
22058 var w = segmentWs.pfValue[s];
22059 var d = segmentDs.pfValue[s];
22060 var w1 = 1 - w;
22061 var w2 = w;
22062 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22063 var adjustedMidpt = {
22064 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22065 y: midptPts.y1 * w1 + midptPts.y2 * w2
22066 };
22067 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22068 }
22069};
22070
22071BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22072 // Self-edge
22073 var rs = edge._private.rscratch;
22074 var dirCounts = pairInfo.dirCounts,
22075 srcPos = pairInfo.srcPos;
22076 var ctrlptDists = edge.pstyle('control-point-distances');
22077 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22078 var loopDir = edge.pstyle('loop-direction').pfValue;
22079 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22080 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22081 rs.edgeType = 'self';
22082 var j = i;
22083 var loopDist = stepSize;
22084
22085 if (edgeIsUnbundled) {
22086 j = 0;
22087 loopDist = ctrlptDist;
22088 }
22089
22090 var loopAngle = loopDir - Math.PI / 2;
22091 var outAngle = loopAngle - loopSwp / 2;
22092 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22093
22094 var dc = String(loopDir + '_' + loopSwp);
22095 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22096 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)];
22097};
22098
22099BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22100 // Compound edge
22101 var rs = edge._private.rscratch;
22102 rs.edgeType = 'compound';
22103 var srcPos = pairInfo.srcPos,
22104 tgtPos = pairInfo.tgtPos,
22105 srcW = pairInfo.srcW,
22106 srcH = pairInfo.srcH,
22107 tgtW = pairInfo.tgtW,
22108 tgtH = pairInfo.tgtH;
22109 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22110 var ctrlptDists = edge.pstyle('control-point-distances');
22111 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22112 var j = i;
22113 var loopDist = stepSize;
22114
22115 if (edgeIsUnbundled) {
22116 j = 0;
22117 loopDist = ctrlptDist;
22118 }
22119
22120 var loopW = 50;
22121 var loopaPos = {
22122 x: srcPos.x - srcW / 2,
22123 y: srcPos.y - srcH / 2
22124 };
22125 var loopbPos = {
22126 x: tgtPos.x - tgtW / 2,
22127 y: tgtPos.y - tgtH / 2
22128 };
22129 var loopPos = {
22130 x: Math.min(loopaPos.x, loopbPos.x),
22131 y: Math.min(loopaPos.y, loopbPos.y)
22132 }; // avoids cases with impossible beziers
22133
22134 var minCompoundStretch = 0.5;
22135 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22136 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22137 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];
22138};
22139
22140BRp$3.findStraightEdgePoints = function (edge) {
22141 // Straight edge within bundle
22142 edge._private.rscratch.edgeType = 'straight';
22143};
22144
22145BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22146 var rs = edge._private.rscratch;
22147 var vectorNormInverse = pairInfo.vectorNormInverse,
22148 posPts = pairInfo.posPts,
22149 intersectionPts = pairInfo.intersectionPts;
22150 var edgeDistances = edge.pstyle('edge-distances').value;
22151 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22152 var ctrlptDists = edge.pstyle('control-point-distances');
22153 var ctrlptWs = edge.pstyle('control-point-weights');
22154 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22155 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22156 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22157
22158 var multi = edgeIsUnbundled;
22159 rs.edgeType = multi ? 'multibezier' : 'bezier';
22160 rs.ctrlpts = [];
22161
22162 for (var b = 0; b < bezierN; b++) {
22163 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22164 var manctrlptDist = void 0;
22165 var sign = signum(normctrlptDist);
22166
22167 if (multi) {
22168 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22169
22170 ctrlptWeight = ctrlptWs.value[b];
22171 }
22172
22173 if (edgeIsUnbundled) {
22174 // multi or single unbundled
22175 manctrlptDist = ctrlptDist;
22176 } else {
22177 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22178 }
22179
22180 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22181 var w1 = 1 - ctrlptWeight;
22182 var w2 = ctrlptWeight;
22183 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22184 var adjustedMidpt = {
22185 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22186 y: midptPts.y1 * w1 + midptPts.y2 * w2
22187 };
22188 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22189 }
22190};
22191
22192BRp$3.findTaxiPoints = function (edge, pairInfo) {
22193 // Taxicab geometry with two turns maximum
22194 var rs = edge._private.rscratch;
22195 rs.edgeType = 'segments';
22196 var VERTICAL = 'vertical';
22197 var HORIZONTAL = 'horizontal';
22198 var LEFTWARD = 'leftward';
22199 var RIGHTWARD = 'rightward';
22200 var DOWNWARD = 'downward';
22201 var UPWARD = 'upward';
22202 var AUTO = 'auto';
22203 var posPts = pairInfo.posPts,
22204 srcW = pairInfo.srcW,
22205 srcH = pairInfo.srcH,
22206 tgtW = pairInfo.tgtW,
22207 tgtH = pairInfo.tgtH;
22208 var edgeDistances = edge.pstyle('edge-distances').value;
22209 var dIncludesNodeBody = edgeDistances !== 'node-position';
22210 var taxiDir = edge.pstyle('taxi-direction').value;
22211 var rawTaxiDir = taxiDir; // unprocessed value
22212
22213 var taxiTurn = edge.pstyle('taxi-turn');
22214 var turnIsPercent = taxiTurn.units === '%';
22215 var taxiTurnPfVal = turnIsPercent && taxiTurn.pfValue < 0 ? 1 + taxiTurn.pfValue : taxiTurn.pfValue;
22216 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22217 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22218 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22219 var pdx = posPts.x2 - posPts.x1;
22220 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
22221
22222 var subDWH = function subDWH(dxy, dwh) {
22223 if (dxy > 0) {
22224 return Math.max(dxy - dwh, 0);
22225 } else {
22226 return Math.min(dxy + dwh, 0);
22227 }
22228 };
22229
22230 var dx = subDWH(pdx, dw);
22231 var dy = subDWH(pdy, dh);
22232 var isExplicitDir = false;
22233
22234 if (taxiDir === AUTO) {
22235 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22236 } else if (taxiDir === UPWARD || taxiDir === DOWNWARD) {
22237 taxiDir = VERTICAL;
22238 isExplicitDir = true;
22239 } else if (taxiDir === LEFTWARD || taxiDir === RIGHTWARD) {
22240 taxiDir = HORIZONTAL;
22241 isExplicitDir = true;
22242 }
22243
22244 var isVert = taxiDir === VERTICAL;
22245 var l = isVert ? dy : dx;
22246 var pl = isVert ? pdy : pdx;
22247 var sgnL = signum(pl);
22248 var forcedDir = false;
22249
22250 if (!(isExplicitDir && turnIsPercent) // forcing in this case would cause weird growing in the opposite direction
22251 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22252 sgnL *= -1;
22253 l = sgnL * Math.abs(l);
22254 forcedDir = true;
22255 }
22256
22257 var d = turnIsPercent ? taxiTurnPfVal * l : taxiTurnPfVal * sgnL;
22258
22259 var getIsTooClose = function getIsTooClose(d) {
22260 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22261 };
22262
22263 var isTooCloseSrc = getIsTooClose(d);
22264 var isTooCloseTgt = getIsTooClose(l - Math.abs(d));
22265 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22266
22267 if (isTooClose && !forcedDir) {
22268 // non-ideal routing
22269 if (isVert) {
22270 // vertical fallbacks
22271 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22272 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22273
22274 if (lShapeInsideSrc) {
22275 // horizontal Z-shape (direction not respected)
22276 var x = (posPts.x1 + posPts.x2) / 2;
22277 var y1 = posPts.y1,
22278 y2 = posPts.y2;
22279 rs.segpts = [x, y1, x, y2];
22280 } else if (lShapeInsideTgt) {
22281 // vertical Z-shape (distance not respected)
22282 var y = (posPts.y1 + posPts.y2) / 2;
22283 var x1 = posPts.x1,
22284 x2 = posPts.x2;
22285 rs.segpts = [x1, y, x2, y];
22286 } else {
22287 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22288 rs.segpts = [posPts.x1, posPts.y2];
22289 }
22290 } else {
22291 // horizontal fallbacks
22292 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22293
22294 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22295
22296 if (_lShapeInsideSrc) {
22297 // vertical Z-shape (direction not respected)
22298 var _y = (posPts.y1 + posPts.y2) / 2;
22299
22300 var _x = posPts.x1,
22301 _x2 = posPts.x2;
22302 rs.segpts = [_x, _y, _x2, _y];
22303 } else if (_lShapeInsideTgt) {
22304 // horizontal Z-shape (turn distance not respected)
22305 var _x3 = (posPts.x1 + posPts.x2) / 2;
22306
22307 var _y2 = posPts.y1,
22308 _y3 = posPts.y2;
22309 rs.segpts = [_x3, _y2, _x3, _y3];
22310 } else {
22311 // L-shape (turn distance not respected, but works well for tree siblings)
22312 rs.segpts = [posPts.x2, posPts.y1];
22313 }
22314 }
22315 } else {
22316 // ideal routing
22317 if (isVert) {
22318 var _y4 = (d < 0 ? posPts.y2 : posPts.y1) + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22319
22320 var _x4 = posPts.x1,
22321 _x5 = posPts.x2;
22322 rs.segpts = [_x4, _y4, _x5, _y4];
22323 } else {
22324 // horizontal
22325 var _x6 = (d < 0 ? posPts.x2 : posPts.x1) + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22326
22327 var _y5 = posPts.y1,
22328 _y6 = posPts.y2;
22329 rs.segpts = [_x6, _y5, _x6, _y6];
22330 }
22331 }
22332};
22333
22334BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22335 var rs = edge._private.rscratch; // can only correct beziers for now...
22336
22337 if (rs.edgeType === 'bezier') {
22338 var srcPos = pairInfo.srcPos,
22339 tgtPos = pairInfo.tgtPos,
22340 srcW = pairInfo.srcW,
22341 srcH = pairInfo.srcH,
22342 tgtW = pairInfo.tgtW,
22343 tgtH = pairInfo.tgtH,
22344 srcShape = pairInfo.srcShape,
22345 tgtShape = pairInfo.tgtShape;
22346 var badStart = !number(rs.startX) || !number(rs.startY);
22347 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
22348 var badEnd = !number(rs.endX) || !number(rs.endY);
22349 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
22350 var minCpADistFactor = 3;
22351 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22352 var minCpADist = minCpADistFactor * arrowW;
22353 var startACpDist = dist({
22354 x: rs.ctrlpts[0],
22355 y: rs.ctrlpts[1]
22356 }, {
22357 x: rs.startX,
22358 y: rs.startY
22359 });
22360 var closeStartACp = startACpDist < minCpADist;
22361 var endACpDist = dist({
22362 x: rs.ctrlpts[0],
22363 y: rs.ctrlpts[1]
22364 }, {
22365 x: rs.endX,
22366 y: rs.endY
22367 });
22368 var closeEndACp = endACpDist < minCpADist;
22369 var overlapping = false;
22370
22371 if (badStart || badAStart || closeStartACp) {
22372 overlapping = true; // project control point along line from src centre to outside the src shape
22373 // (otherwise intersection will yield nothing)
22374
22375 var cpD = {
22376 // delta
22377 x: rs.ctrlpts[0] - srcPos.x,
22378 y: rs.ctrlpts[1] - srcPos.y
22379 };
22380 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22381
22382 var cpM = {
22383 // normalised delta
22384 x: cpD.x / cpL,
22385 y: cpD.y / cpL
22386 };
22387 var radius = Math.max(srcW, srcH);
22388 var cpProj = {
22389 // *2 radius guarantees outside shape
22390 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22391 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22392 };
22393 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
22394
22395 if (closeStartACp) {
22396 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22397 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22398 } else {
22399 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22400 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22401 }
22402 }
22403
22404 if (badEnd || badAEnd || closeEndACp) {
22405 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
22406 // (otherwise intersection will yield nothing)
22407
22408 var _cpD = {
22409 // delta
22410 x: rs.ctrlpts[0] - tgtPos.x,
22411 y: rs.ctrlpts[1] - tgtPos.y
22412 };
22413
22414 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22415
22416
22417 var _cpM = {
22418 // normalised delta
22419 x: _cpD.x / _cpL,
22420 y: _cpD.y / _cpL
22421 };
22422
22423 var _radius = Math.max(srcW, srcH);
22424
22425 var _cpProj = {
22426 // *2 radius guarantees outside shape
22427 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22428 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22429 };
22430 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
22431
22432 if (closeEndACp) {
22433 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22434 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22435 } else {
22436 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22437 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22438 }
22439 }
22440
22441 if (overlapping) {
22442 // recalc endpts
22443 this.findEndpoints(edge);
22444 }
22445 }
22446};
22447
22448BRp$3.storeAllpts = function (edge) {
22449 var rs = edge._private.rscratch;
22450
22451 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22452 rs.allpts = [];
22453 rs.allpts.push(rs.startX, rs.startY);
22454
22455 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22456 // ctrl pt itself
22457 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
22458
22459 if (b + 3 < rs.ctrlpts.length) {
22460 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22461 }
22462 }
22463
22464 rs.allpts.push(rs.endX, rs.endY);
22465 var m, mt;
22466
22467 if (rs.ctrlpts.length / 2 % 2 === 0) {
22468 m = rs.allpts.length / 2 - 1;
22469 rs.midX = rs.allpts[m];
22470 rs.midY = rs.allpts[m + 1];
22471 } else {
22472 m = rs.allpts.length / 2 - 3;
22473 mt = 0.5;
22474 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22475 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22476 }
22477 } else if (rs.edgeType === 'straight') {
22478 // need to calc these after endpts
22479 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
22480
22481 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22482 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22483 } else if (rs.edgeType === 'segments') {
22484 rs.allpts = [];
22485 rs.allpts.push(rs.startX, rs.startY);
22486 rs.allpts.push.apply(rs.allpts, rs.segpts);
22487 rs.allpts.push(rs.endX, rs.endY);
22488
22489 if (rs.segpts.length % 4 === 0) {
22490 var i2 = rs.segpts.length / 2;
22491 var i1 = i2 - 2;
22492 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22493 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22494 } else {
22495 var _i = rs.segpts.length / 2 - 1;
22496
22497 rs.midX = rs.segpts[_i];
22498 rs.midY = rs.segpts[_i + 1];
22499 }
22500 }
22501};
22502
22503BRp$3.checkForInvalidEdgeWarning = function (edge) {
22504 var rs = edge[0]._private.rscratch;
22505
22506 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
22507 rs.loggedErr = false;
22508 } else {
22509 if (!rs.loggedErr) {
22510 rs.loggedErr = true;
22511 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.');
22512 }
22513 }
22514};
22515
22516BRp$3.findEdgeControlPoints = function (edges) {
22517 var _this = this;
22518
22519 if (!edges || edges.length === 0) {
22520 return;
22521 }
22522
22523 var r = this;
22524 var cy = r.cy;
22525 var hasCompounds = cy.hasCompoundNodes();
22526 var hashTable = {
22527 map: new Map$1(),
22528 get: function get(pairId) {
22529 var map2 = this.map.get(pairId[0]);
22530
22531 if (map2 != null) {
22532 return map2.get(pairId[1]);
22533 } else {
22534 return null;
22535 }
22536 },
22537 set: function set(pairId, val) {
22538 var map2 = this.map.get(pairId[0]);
22539
22540 if (map2 == null) {
22541 map2 = new Map$1();
22542 this.map.set(pairId[0], map2);
22543 }
22544
22545 map2.set(pairId[1], val);
22546 }
22547 };
22548 var pairIds = [];
22549 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
22550
22551 for (var i = 0; i < edges.length; i++) {
22552 var edge = edges[i];
22553 var _p = edge._private;
22554 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
22555 // they shouldn't take up space
22556
22557 if (edge.removed() || !edge.takesUpSpace()) {
22558 continue;
22559 }
22560
22561 if (curveStyle === 'haystack') {
22562 haystackEdges.push(edge);
22563 continue;
22564 }
22565
22566 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
22567 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
22568 var src = _p.source;
22569 var tgt = _p.target;
22570 var srcIndex = src.poolIndex();
22571 var tgtIndex = tgt.poolIndex();
22572 var pairId = [srcIndex, tgtIndex].sort();
22573 var tableEntry = hashTable.get(pairId);
22574
22575 if (tableEntry == null) {
22576 tableEntry = {
22577 eles: []
22578 };
22579 hashTable.set(pairId, tableEntry);
22580 pairIds.push(pairId);
22581 }
22582
22583 tableEntry.eles.push(edge);
22584
22585 if (edgeIsUnbundled) {
22586 tableEntry.hasUnbundled = true;
22587 }
22588
22589 if (edgeIsBezier) {
22590 tableEntry.hasBezier = true;
22591 }
22592 } // for each pair (src, tgt), create the ctrl pts
22593 // Nested for loop is OK; total number of iterations for both loops = edgeCount
22594
22595
22596 var _loop = function _loop(p) {
22597 var pairId = pairIds[p];
22598 var pairInfo = hashTable.get(pairId);
22599 var swappedpairInfo = void 0;
22600
22601 if (!pairInfo.hasUnbundled) {
22602 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
22603 return e.isBundledBezier();
22604 });
22605 clearArray(pairInfo.eles);
22606 pllEdges.forEach(function (edge) {
22607 return pairInfo.eles.push(edge);
22608 }); // for each pair id, the edges should be sorted by index
22609
22610 pairInfo.eles.sort(function (edge1, edge2) {
22611 return edge1.poolIndex() - edge2.poolIndex();
22612 });
22613 }
22614
22615 var firstEdge = pairInfo.eles[0];
22616 var src = firstEdge.source();
22617 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
22618
22619 if (src.poolIndex() > tgt.poolIndex()) {
22620 var temp = src;
22621 src = tgt;
22622 tgt = temp;
22623 }
22624
22625 var srcPos = pairInfo.srcPos = src.position();
22626 var tgtPos = pairInfo.tgtPos = tgt.position();
22627 var srcW = pairInfo.srcW = src.outerWidth();
22628 var srcH = pairInfo.srcH = src.outerHeight();
22629 var tgtW = pairInfo.tgtW = tgt.outerWidth();
22630 var tgtH = pairInfo.tgtH = tgt.outerHeight();
22631
22632 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
22633
22634 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
22635
22636 pairInfo.dirCounts = {
22637 'north': 0,
22638 'west': 0,
22639 'south': 0,
22640 'east': 0,
22641 'northwest': 0,
22642 'southwest': 0,
22643 'northeast': 0,
22644 'southeast': 0
22645 };
22646
22647 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
22648 var _edge = pairInfo.eles[_i2];
22649 var rs = _edge[0]._private.rscratch;
22650
22651 var _curveStyle = _edge.pstyle('curve-style').value;
22652
22653 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
22654
22655
22656 var edgeIsSwapped = !src.same(_edge.source());
22657
22658 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
22659 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
22660
22661 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
22662 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
22663
22664 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
22665 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
22666 var intersectionPts = pairInfo.intersectionPts = {
22667 x1: srcOutside[0],
22668 x2: tgtOutside[0],
22669 y1: srcOutside[1],
22670 y2: tgtOutside[1]
22671 };
22672 var posPts = pairInfo.posPts = {
22673 x1: srcPos.x,
22674 x2: tgtPos.x,
22675 y1: srcPos.y,
22676 y2: tgtPos.y
22677 };
22678 var dy = tgtOutside[1] - srcOutside[1];
22679 var dx = tgtOutside[0] - srcOutside[0];
22680 var l = Math.sqrt(dx * dx + dy * dy);
22681 var vector = pairInfo.vector = {
22682 x: dx,
22683 y: dy
22684 };
22685 var vectorNorm = pairInfo.vectorNorm = {
22686 x: vector.x / l,
22687 y: vector.y / l
22688 };
22689 var vectorNormInverse = {
22690 x: -vectorNorm.y,
22691 y: vectorNorm.x
22692 }; // if node shapes overlap, then no ctrl pts to draw
22693
22694 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);
22695 pairInfo.vectorNormInverse = vectorNormInverse;
22696 swappedpairInfo = {
22697 nodesOverlap: pairInfo.nodesOverlap,
22698 dirCounts: pairInfo.dirCounts,
22699 calculatedIntersection: true,
22700 hasBezier: pairInfo.hasBezier,
22701 hasUnbundled: pairInfo.hasUnbundled,
22702 eles: pairInfo.eles,
22703 srcPos: tgtPos,
22704 tgtPos: srcPos,
22705 srcW: tgtW,
22706 srcH: tgtH,
22707 tgtW: srcW,
22708 tgtH: srcH,
22709 srcIntn: tgtIntn,
22710 tgtIntn: srcIntn,
22711 srcShape: tgtShape,
22712 tgtShape: srcShape,
22713 posPts: {
22714 x1: posPts.x2,
22715 y1: posPts.y2,
22716 x2: posPts.x1,
22717 y2: posPts.y1
22718 },
22719 intersectionPts: {
22720 x1: intersectionPts.x2,
22721 y1: intersectionPts.y2,
22722 x2: intersectionPts.x1,
22723 y2: intersectionPts.y1
22724 },
22725 vector: {
22726 x: -vector.x,
22727 y: -vector.y
22728 },
22729 vectorNorm: {
22730 x: -vectorNorm.x,
22731 y: -vectorNorm.y
22732 },
22733 vectorNormInverse: {
22734 x: -vectorNormInverse.x,
22735 y: -vectorNormInverse.y
22736 }
22737 };
22738 }
22739
22740 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
22741 rs.nodesOverlap = passedPairInfo.nodesOverlap;
22742 rs.srcIntn = passedPairInfo.srcIntn;
22743 rs.tgtIntn = passedPairInfo.tgtIntn;
22744
22745 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
22746 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22747 } else if (src === tgt) {
22748 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22749 } else if (_curveStyle === 'segments') {
22750 _this.findSegmentsPoints(_edge, passedPairInfo);
22751 } else if (_curveStyle === 'taxi') {
22752 _this.findTaxiPoints(_edge, passedPairInfo);
22753 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
22754 _this.findStraightEdgePoints(_edge);
22755 } else {
22756 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
22757 }
22758
22759 _this.findEndpoints(_edge);
22760
22761 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
22762
22763 _this.checkForInvalidEdgeWarning(_edge);
22764
22765 _this.storeAllpts(_edge);
22766
22767 _this.storeEdgeProjections(_edge);
22768
22769 _this.calculateArrowAngles(_edge);
22770
22771 _this.recalculateEdgeLabelProjections(_edge);
22772
22773 _this.calculateLabelAngles(_edge);
22774 } // for pair edges
22775
22776 };
22777
22778 for (var p = 0; p < pairIds.length; p++) {
22779 _loop(p);
22780 } // for pair ids
22781 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
22782
22783
22784 this.findHaystackPoints(haystackEdges);
22785};
22786
22787function getPts(pts) {
22788 var retPts = [];
22789
22790 if (pts == null) {
22791 return;
22792 }
22793
22794 for (var i = 0; i < pts.length; i += 2) {
22795 var x = pts[i];
22796 var y = pts[i + 1];
22797 retPts.push({
22798 x: x,
22799 y: y
22800 });
22801 }
22802
22803 return retPts;
22804}
22805
22806BRp$3.getSegmentPoints = function (edge) {
22807 var rs = edge[0]._private.rscratch;
22808 var type = rs.edgeType;
22809
22810 if (type === 'segments') {
22811 this.recalculateRenderedStyle(edge);
22812 return getPts(rs.segpts);
22813 }
22814};
22815
22816BRp$3.getControlPoints = function (edge) {
22817 var rs = edge[0]._private.rscratch;
22818 var type = rs.edgeType;
22819
22820 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
22821 this.recalculateRenderedStyle(edge);
22822 return getPts(rs.ctrlpts);
22823 }
22824};
22825
22826BRp$3.getEdgeMidpoint = function (edge) {
22827 var rs = edge[0]._private.rscratch;
22828 this.recalculateRenderedStyle(edge);
22829 return {
22830 x: rs.midX,
22831 y: rs.midY
22832 };
22833};
22834
22835var BRp$4 = {};
22836
22837BRp$4.manualEndptToPx = function (node, prop) {
22838 var r = this;
22839 var npos = node.position();
22840 var w = node.outerWidth();
22841 var h = node.outerHeight();
22842
22843 if (prop.value.length === 2) {
22844 var p = [prop.pfValue[0], prop.pfValue[1]];
22845
22846 if (prop.units[0] === '%') {
22847 p[0] = p[0] * w;
22848 }
22849
22850 if (prop.units[1] === '%') {
22851 p[1] = p[1] * h;
22852 }
22853
22854 p[0] += npos.x;
22855 p[1] += npos.y;
22856 return p;
22857 } else {
22858 var angle = prop.pfValue[0];
22859 angle = -Math.PI / 2 + angle; // start at 12 o'clock
22860
22861 var l = 2 * Math.max(w, h);
22862 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
22863 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
22864 }
22865};
22866
22867BRp$4.findEndpoints = function (edge) {
22868 var r = this;
22869 var intersect;
22870 var source = edge.source()[0];
22871 var target = edge.target()[0];
22872 var srcPos = source.position();
22873 var tgtPos = target.position();
22874 var tgtArShape = edge.pstyle('target-arrow-shape').value;
22875 var srcArShape = edge.pstyle('source-arrow-shape').value;
22876 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
22877 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
22878 var curveStyle = edge.pstyle('curve-style').value;
22879 var rs = edge._private.rscratch;
22880 var et = rs.edgeType;
22881 var taxi = curveStyle === 'taxi';
22882 var self = et === 'self' || et === 'compound';
22883 var bezier = et === 'bezier' || et === 'multibezier' || self;
22884 var multi = et !== 'bezier';
22885 var lines = et === 'straight' || et === 'segments';
22886 var segments = et === 'segments';
22887 var hasEndpts = bezier || multi || lines;
22888 var overrideEndpts = self || taxi;
22889 var srcManEndpt = edge.pstyle('source-endpoint');
22890 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
22891 var tgtManEndpt = edge.pstyle('target-endpoint');
22892 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
22893 rs.srcManEndpt = srcManEndpt;
22894 rs.tgtManEndpt = tgtManEndpt;
22895 var p1; // last known point of edge on target side
22896
22897 var p2; // last known point of edge on source side
22898
22899 var p1_i; // point to intersect with target shape
22900
22901 var p2_i; // point to intersect with source shape
22902
22903 if (bezier) {
22904 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
22905 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
22906 p1 = cpEnd;
22907 p2 = cpStart;
22908 } else if (lines) {
22909 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
22910 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
22911 p1 = tgtArrowFromPt;
22912 p2 = srcArrowFromPt;
22913 }
22914
22915 if (tgtManEndptVal === 'inside-to-node') {
22916 intersect = [tgtPos.x, tgtPos.y];
22917 } else if (tgtManEndpt.units) {
22918 intersect = this.manualEndptToPx(target, tgtManEndpt);
22919 } else if (tgtManEndptVal === 'outside-to-line') {
22920 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
22921 } else {
22922 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
22923 p1_i = p1;
22924 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
22925 p1_i = [srcPos.x, srcPos.y];
22926 }
22927
22928 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
22929
22930 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
22931 var trs = target._private.rscratch;
22932 var lw = trs.labelWidth;
22933 var lh = trs.labelHeight;
22934 var lx = trs.labelX;
22935 var ly = trs.labelY;
22936 var lw2 = lw / 2;
22937 var lh2 = lh / 2;
22938 var va = target.pstyle('text-valign').value;
22939
22940 if (va === 'top') {
22941 ly -= lh2;
22942 } else if (va === 'bottom') {
22943 ly += lh2;
22944 }
22945
22946 var ha = target.pstyle('text-halign').value;
22947
22948 if (ha === 'left') {
22949 lx -= lw2;
22950 } else if (ha === 'right') {
22951 lx += lw2;
22952 }
22953
22954 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);
22955
22956 if (labelIntersect.length > 0) {
22957 var refPt = srcPos;
22958 var intSqdist = sqdist(refPt, array2point(intersect));
22959 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
22960 var minSqDist = intSqdist;
22961
22962 if (labIntSqdist < intSqdist) {
22963 intersect = labelIntersect;
22964 minSqDist = labIntSqdist;
22965 }
22966
22967 if (labelIntersect.length > 2) {
22968 var labInt2SqDist = sqdist(refPt, {
22969 x: labelIntersect[2],
22970 y: labelIntersect[3]
22971 });
22972
22973 if (labInt2SqDist < minSqDist) {
22974 intersect = [labelIntersect[2], labelIntersect[3]];
22975 }
22976 }
22977 }
22978 }
22979 }
22980
22981 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
22982 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
22983 rs.endX = edgeEnd[0];
22984 rs.endY = edgeEnd[1];
22985 rs.arrowEndX = arrowEnd[0];
22986 rs.arrowEndY = arrowEnd[1];
22987
22988 if (srcManEndptVal === 'inside-to-node') {
22989 intersect = [srcPos.x, srcPos.y];
22990 } else if (srcManEndpt.units) {
22991 intersect = this.manualEndptToPx(source, srcManEndpt);
22992 } else if (srcManEndptVal === 'outside-to-line') {
22993 intersect = rs.srcIntn; // use cached value from ctrlpt calc
22994 } else {
22995 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
22996 p2_i = p2;
22997 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
22998 p2_i = [tgtPos.x, tgtPos.y];
22999 }
23000
23001 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23002
23003 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23004 var srs = source._private.rscratch;
23005 var _lw = srs.labelWidth;
23006 var _lh = srs.labelHeight;
23007 var _lx = srs.labelX;
23008 var _ly = srs.labelY;
23009
23010 var _lw2 = _lw / 2;
23011
23012 var _lh2 = _lh / 2;
23013
23014 var _va = source.pstyle('text-valign').value;
23015
23016 if (_va === 'top') {
23017 _ly -= _lh2;
23018 } else if (_va === 'bottom') {
23019 _ly += _lh2;
23020 }
23021
23022 var _ha = source.pstyle('text-halign').value;
23023
23024 if (_ha === 'left') {
23025 _lx -= _lw2;
23026 } else if (_ha === 'right') {
23027 _lx += _lw2;
23028 }
23029
23030 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);
23031
23032 if (_labelIntersect.length > 0) {
23033 var _refPt = tgtPos;
23034
23035 var _intSqdist = sqdist(_refPt, array2point(intersect));
23036
23037 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23038
23039 var _minSqDist = _intSqdist;
23040
23041 if (_labIntSqdist < _intSqdist) {
23042 intersect = [_labelIntersect[0], _labelIntersect[1]];
23043 _minSqDist = _labIntSqdist;
23044 }
23045
23046 if (_labelIntersect.length > 2) {
23047 var _labInt2SqDist = sqdist(_refPt, {
23048 x: _labelIntersect[2],
23049 y: _labelIntersect[3]
23050 });
23051
23052 if (_labInt2SqDist < _minSqDist) {
23053 intersect = [_labelIntersect[2], _labelIntersect[3]];
23054 }
23055 }
23056 }
23057 }
23058 }
23059
23060 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23061 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23062 rs.startX = edgeStart[0];
23063 rs.startY = edgeStart[1];
23064 rs.arrowStartX = arrowStart[0];
23065 rs.arrowStartY = arrowStart[1];
23066
23067 if (hasEndpts) {
23068 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
23069 rs.badLine = true;
23070 } else {
23071 rs.badLine = false;
23072 }
23073 }
23074};
23075
23076BRp$4.getSourceEndpoint = function (edge) {
23077 var rs = edge[0]._private.rscratch;
23078 this.recalculateRenderedStyle(edge);
23079
23080 switch (rs.edgeType) {
23081 case 'haystack':
23082 return {
23083 x: rs.haystackPts[0],
23084 y: rs.haystackPts[1]
23085 };
23086
23087 default:
23088 return {
23089 x: rs.arrowStartX,
23090 y: rs.arrowStartY
23091 };
23092 }
23093};
23094
23095BRp$4.getTargetEndpoint = function (edge) {
23096 var rs = edge[0]._private.rscratch;
23097 this.recalculateRenderedStyle(edge);
23098
23099 switch (rs.edgeType) {
23100 case 'haystack':
23101 return {
23102 x: rs.haystackPts[2],
23103 y: rs.haystackPts[3]
23104 };
23105
23106 default:
23107 return {
23108 x: rs.arrowEndX,
23109 y: rs.arrowEndY
23110 };
23111 }
23112};
23113
23114var BRp$5 = {};
23115
23116function pushBezierPts(r, edge, pts) {
23117 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23118 return qbezierAt(p1, p2, p3, t);
23119 };
23120
23121 var _p = edge._private;
23122 var bpts = _p.rstyle.bezierPts;
23123
23124 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23125 var p = r.bezierProjPcts[i];
23126 bpts.push({
23127 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23128 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23129 });
23130 }
23131}
23132
23133BRp$5.storeEdgeProjections = function (edge) {
23134 var _p = edge._private;
23135 var rs = _p.rscratch;
23136 var et = rs.edgeType; // clear the cached points state
23137
23138 _p.rstyle.bezierPts = null;
23139 _p.rstyle.linePts = null;
23140 _p.rstyle.haystackPts = null;
23141
23142 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23143 _p.rstyle.bezierPts = [];
23144
23145 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23146 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23147 }
23148 } else if (et === 'segments') {
23149 var lpts = _p.rstyle.linePts = [];
23150
23151 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23152 lpts.push({
23153 x: rs.allpts[i],
23154 y: rs.allpts[i + 1]
23155 });
23156 }
23157 } else if (et === 'haystack') {
23158 var hpts = rs.haystackPts;
23159 _p.rstyle.haystackPts = [{
23160 x: hpts[0],
23161 y: hpts[1]
23162 }, {
23163 x: hpts[2],
23164 y: hpts[3]
23165 }];
23166 }
23167
23168 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23169};
23170
23171BRp$5.recalculateEdgeProjections = function (edges) {
23172 this.findEdgeControlPoints(edges);
23173};
23174
23175var BRp$6 = {};
23176
23177BRp$6.recalculateNodeLabelProjection = function (node) {
23178 var content = node.pstyle('label').strValue;
23179
23180 if (emptyString(content)) {
23181 return;
23182 }
23183
23184 var textX, textY;
23185 var _p = node._private;
23186 var nodeWidth = node.width();
23187 var nodeHeight = node.height();
23188 var padding = node.padding();
23189 var nodePos = node.position();
23190 var textHalign = node.pstyle('text-halign').strValue;
23191 var textValign = node.pstyle('text-valign').strValue;
23192 var rs = _p.rscratch;
23193 var rstyle = _p.rstyle;
23194
23195 switch (textHalign) {
23196 case 'left':
23197 textX = nodePos.x - nodeWidth / 2 - padding;
23198 break;
23199
23200 case 'right':
23201 textX = nodePos.x + nodeWidth / 2 + padding;
23202 break;
23203
23204 default:
23205 // e.g. center
23206 textX = nodePos.x;
23207 }
23208
23209 switch (textValign) {
23210 case 'top':
23211 textY = nodePos.y - nodeHeight / 2 - padding;
23212 break;
23213
23214 case 'bottom':
23215 textY = nodePos.y + nodeHeight / 2 + padding;
23216 break;
23217
23218 default:
23219 // e.g. middle
23220 textY = nodePos.y;
23221 }
23222
23223 rs.labelX = textX;
23224 rs.labelY = textY;
23225 rstyle.labelX = textX;
23226 rstyle.labelY = textY;
23227 this.applyLabelDimensions(node);
23228};
23229
23230var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23231 var angle = Math.atan(dy / dx);
23232
23233 if (dx === 0 && angle < 0) {
23234 angle = angle * -1;
23235 }
23236
23237 return angle;
23238};
23239
23240var lineAngle = function lineAngle(p0, p1) {
23241 var dx = p1.x - p0.x;
23242 var dy = p1.y - p0.y;
23243 return lineAngleFromDelta(dx, dy);
23244};
23245
23246var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23247 var t0 = bound(0, t - 0.001, 1);
23248 var t1 = bound(0, t + 0.001, 1);
23249 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23250 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23251 return lineAngle(lp0, lp1);
23252};
23253
23254BRp$6.recalculateEdgeLabelProjections = function (edge) {
23255 var p;
23256 var _p = edge._private;
23257 var rs = _p.rscratch;
23258 var r = this;
23259 var content = {
23260 mid: edge.pstyle('label').strValue,
23261 source: edge.pstyle('source-label').strValue,
23262 target: edge.pstyle('target-label').strValue
23263 };
23264
23265 if (content.mid || content.source || content.target) ; else {
23266 return; // no labels => no calcs
23267 } // add center point to style so bounding box calculations can use it
23268 //
23269
23270
23271 p = {
23272 x: rs.midX,
23273 y: rs.midY
23274 };
23275
23276 var setRs = function setRs(propName, prefix, value) {
23277 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23278 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23279 };
23280
23281 setRs('labelX', null, p.x);
23282 setRs('labelY', null, p.y);
23283 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23284 setRs('labelAutoAngle', null, midAngle);
23285
23286 var createControlPointInfo = function createControlPointInfo() {
23287 if (createControlPointInfo.cache) {
23288 return createControlPointInfo.cache;
23289 } // use cache so only 1x per edge
23290
23291
23292 var ctrlpts = []; // store each ctrlpt info init
23293
23294 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23295 var p0 = {
23296 x: rs.allpts[i],
23297 y: rs.allpts[i + 1]
23298 };
23299 var p1 = {
23300 x: rs.allpts[i + 2],
23301 y: rs.allpts[i + 3]
23302 }; // ctrlpt
23303
23304 var p2 = {
23305 x: rs.allpts[i + 4],
23306 y: rs.allpts[i + 5]
23307 };
23308 ctrlpts.push({
23309 p0: p0,
23310 p1: p1,
23311 p2: p2,
23312 startDist: 0,
23313 length: 0,
23314 segments: []
23315 });
23316 }
23317
23318 var bpts = _p.rstyle.bezierPts;
23319 var nProjs = r.bezierProjPcts.length;
23320
23321 function addSegment(cp, p0, p1, t0, t1) {
23322 var length = dist(p0, p1);
23323 var prevSegment = cp.segments[cp.segments.length - 1];
23324 var segment = {
23325 p0: p0,
23326 p1: p1,
23327 t0: t0,
23328 t1: t1,
23329 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23330 length: length
23331 };
23332 cp.segments.push(segment);
23333 cp.length += length;
23334 } // update each ctrlpt with segment info
23335
23336
23337 for (var _i = 0; _i < ctrlpts.length; _i++) {
23338 var cp = ctrlpts[_i];
23339 var prevCp = ctrlpts[_i - 1];
23340
23341 if (prevCp) {
23342 cp.startDist = prevCp.startDist + prevCp.length;
23343 }
23344
23345 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23346
23347 for (var j = 0; j < nProjs - 1; j++) {
23348 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23349 }
23350
23351 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23352 }
23353
23354 return createControlPointInfo.cache = ctrlpts;
23355 };
23356
23357 var calculateEndProjection = function calculateEndProjection(prefix) {
23358 var angle;
23359 var isSrc = prefix === 'source';
23360
23361 if (!content[prefix]) {
23362 return;
23363 }
23364
23365 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23366
23367 switch (rs.edgeType) {
23368 case 'self':
23369 case 'compound':
23370 case 'bezier':
23371 case 'multibezier':
23372 {
23373 var cps = createControlPointInfo();
23374 var selected;
23375 var startDist = 0;
23376 var totalDist = 0; // find the segment we're on
23377
23378 for (var i = 0; i < cps.length; i++) {
23379 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23380
23381 for (var j = 0; j < _cp.segments.length; j++) {
23382 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23383 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23384 startDist = totalDist;
23385 totalDist += _seg.length;
23386
23387 if (totalDist >= offset || lastSeg) {
23388 selected = {
23389 cp: _cp,
23390 segment: _seg
23391 };
23392 break;
23393 }
23394 }
23395
23396 if (selected) {
23397 break;
23398 }
23399 }
23400
23401 var cp = selected.cp;
23402 var seg = selected.segment;
23403 var tSegment = (offset - startDist) / seg.length;
23404 var segDt = seg.t1 - seg.t0;
23405 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23406 t = bound(0, t, 1);
23407 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23408 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23409 break;
23410 }
23411
23412 case 'straight':
23413 case 'segments':
23414 case 'haystack':
23415 {
23416 var d = 0,
23417 di,
23418 d0;
23419 var p0, p1;
23420 var l = rs.allpts.length;
23421
23422 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23423 if (isSrc) {
23424 p0 = {
23425 x: rs.allpts[_i2],
23426 y: rs.allpts[_i2 + 1]
23427 };
23428 p1 = {
23429 x: rs.allpts[_i2 + 2],
23430 y: rs.allpts[_i2 + 3]
23431 };
23432 } else {
23433 p0 = {
23434 x: rs.allpts[l - 2 - _i2],
23435 y: rs.allpts[l - 1 - _i2]
23436 };
23437 p1 = {
23438 x: rs.allpts[l - 4 - _i2],
23439 y: rs.allpts[l - 3 - _i2]
23440 };
23441 }
23442
23443 di = dist(p0, p1);
23444 d0 = d;
23445 d += di;
23446
23447 if (d >= offset) {
23448 break;
23449 }
23450 }
23451
23452 var pD = offset - d0;
23453
23454 var _t = pD / di;
23455
23456 _t = bound(0, _t, 1);
23457 p = lineAt(p0, p1, _t);
23458 angle = lineAngle(p0, p1);
23459 break;
23460 }
23461 }
23462
23463 setRs('labelX', prefix, p.x);
23464 setRs('labelY', prefix, p.y);
23465 setRs('labelAutoAngle', prefix, angle);
23466 };
23467
23468 calculateEndProjection('source');
23469 calculateEndProjection('target');
23470 this.applyLabelDimensions(edge);
23471};
23472
23473BRp$6.applyLabelDimensions = function (ele) {
23474 this.applyPrefixedLabelDimensions(ele);
23475
23476 if (ele.isEdge()) {
23477 this.applyPrefixedLabelDimensions(ele, 'source');
23478 this.applyPrefixedLabelDimensions(ele, 'target');
23479 }
23480};
23481
23482BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
23483 var _p = ele._private;
23484 var text = this.getLabelText(ele, prefix);
23485 var labelDims = this.calculateLabelDimensions(ele, text);
23486 var lineHeight = ele.pstyle('line-height').pfValue;
23487 var textWrap = ele.pstyle('text-wrap').strValue;
23488 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23489 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23490 var normPerLineHeight = labelDims.height / numLines;
23491 var labelLineHeight = normPerLineHeight * lineHeight;
23492 var width = labelDims.width;
23493 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23494 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23495 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23496 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23497 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23498 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23499};
23500
23501BRp$6.getLabelText = function (ele, prefix) {
23502 var _p = ele._private;
23503 var pfd = prefix ? prefix + '-' : '';
23504 var text = ele.pstyle(pfd + 'label').strValue;
23505 var textTransform = ele.pstyle('text-transform').value;
23506
23507 var rscratch = function rscratch(propName, value) {
23508 if (value) {
23509 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23510 return value;
23511 } else {
23512 return getPrefixedProperty(_p.rscratch, propName, prefix);
23513 }
23514 }; // for empty text, skip all processing
23515
23516
23517 if (!text) {
23518 return '';
23519 }
23520
23521 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23522 text = text.toUpperCase();
23523 } else if (textTransform == 'lowercase') {
23524 text = text.toLowerCase();
23525 }
23526
23527 var wrapStyle = ele.pstyle('text-wrap').value;
23528
23529 if (wrapStyle === 'wrap') {
23530 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
23531
23532 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23533 return rscratch('labelWrapCachedText');
23534 }
23535
23536 var zwsp = "\u200B";
23537 var lines = text.split('\n');
23538 var maxW = ele.pstyle('text-max-width').pfValue;
23539 var overflow = ele.pstyle('text-overflow-wrap').value;
23540 var overflowAny = overflow === 'anywhere';
23541 var wrappedLines = [];
23542 var wordsRegex = /[\s\u200b]+/;
23543 var wordSeparator = overflowAny ? '' : ' ';
23544
23545 for (var l = 0; l < lines.length; l++) {
23546 var line = lines[l];
23547 var lineDims = this.calculateLabelDimensions(ele, line);
23548 var lineW = lineDims.width;
23549
23550 if (overflowAny) {
23551 var processedLine = line.split('').join(zwsp);
23552 line = processedLine;
23553 }
23554
23555 if (lineW > maxW) {
23556 // line is too long
23557 var words = line.split(wordsRegex);
23558 var subline = '';
23559
23560 for (var w = 0; w < words.length; w++) {
23561 var word = words[w];
23562 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
23563 var testDims = this.calculateLabelDimensions(ele, testLine);
23564 var testW = testDims.width;
23565
23566 if (testW <= maxW) {
23567 // word fits on current line
23568 subline += word + wordSeparator;
23569 } else {
23570 // word starts new line
23571 if (subline) {
23572 wrappedLines.push(subline);
23573 }
23574
23575 subline = word + wordSeparator;
23576 }
23577 } // if there's remaining text, put it in a wrapped line
23578
23579
23580 if (!subline.match(/^[\s\u200b]+$/)) {
23581 wrappedLines.push(subline);
23582 }
23583 } else {
23584 // line is already short enough
23585 wrappedLines.push(line);
23586 }
23587 } // for
23588
23589
23590 rscratch('labelWrapCachedLines', wrappedLines);
23591 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23592 rscratch('labelWrapKey', labelKey);
23593 } else if (wrapStyle === 'ellipsis') {
23594 var _maxW = ele.pstyle('text-max-width').pfValue;
23595 var ellipsized = '';
23596 var ellipsis = "\u2026";
23597 var incLastCh = false;
23598
23599 for (var i = 0; i < text.length; i++) {
23600 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23601
23602 if (widthWithNextCh > _maxW) {
23603 break;
23604 }
23605
23606 ellipsized += text[i];
23607
23608 if (i === text.length - 1) {
23609 incLastCh = true;
23610 }
23611 }
23612
23613 if (!incLastCh) {
23614 ellipsized += ellipsis;
23615 }
23616
23617 return ellipsized;
23618 } // if ellipsize
23619
23620
23621 return text;
23622};
23623
23624BRp$6.getLabelJustification = function (ele) {
23625 var justification = ele.pstyle('text-justification').strValue;
23626 var textHalign = ele.pstyle('text-halign').strValue;
23627
23628 if (justification === 'auto') {
23629 if (ele.isNode()) {
23630 switch (textHalign) {
23631 case 'left':
23632 return 'right';
23633
23634 case 'right':
23635 return 'left';
23636
23637 default:
23638 return 'center';
23639 }
23640 } else {
23641 return 'center';
23642 }
23643 } else {
23644 return justification;
23645 }
23646};
23647
23648BRp$6.calculateLabelDimensions = function (ele, text) {
23649 var r = this;
23650 var cacheKey = hashString(text, ele._private.labelDimsKey);
23651 var cache = r.labelDimCache || (r.labelDimCache = []);
23652 var existingVal = cache[cacheKey];
23653
23654 if (existingVal != null) {
23655 return existingVal;
23656 }
23657
23658 var sizeMult = 1; // increase the scale to increase accuracy w.r.t. zoomed text
23659
23660 var fStyle = ele.pstyle('font-style').strValue;
23661 var size = sizeMult * ele.pstyle('font-size').pfValue + 'px';
23662 var family = ele.pstyle('font-family').strValue;
23663 var weight = ele.pstyle('font-weight').strValue;
23664 var div = this.labelCalcDiv;
23665
23666 if (!div) {
23667 div = this.labelCalcDiv = document.createElement('div'); // eslint-disable-line no-undef
23668
23669 document.body.appendChild(div); // eslint-disable-line no-undef
23670 }
23671
23672 var ds = div.style; // from ele style
23673
23674 ds.fontFamily = family;
23675 ds.fontStyle = fStyle;
23676 ds.fontSize = size;
23677 ds.fontWeight = weight; // forced style
23678
23679 ds.position = 'absolute';
23680 ds.left = '-9999px';
23681 ds.top = '-9999px';
23682 ds.zIndex = '-1';
23683 ds.visibility = 'hidden';
23684 ds.pointerEvents = 'none';
23685 ds.padding = '0';
23686 ds.lineHeight = '1'; // - newlines must be taken into account for text-wrap:wrap
23687 // - since spaces are not collapsed, each space must be taken into account
23688
23689 ds.whiteSpace = 'pre'; // put label content in div
23690
23691 div.textContent = text;
23692 return cache[cacheKey] = {
23693 width: Math.ceil(div.clientWidth / sizeMult),
23694 height: Math.ceil(div.clientHeight / sizeMult)
23695 };
23696};
23697
23698BRp$6.calculateLabelAngle = function (ele, prefix) {
23699 var _p = ele._private;
23700 var rs = _p.rscratch;
23701 var isEdge = ele.isEdge();
23702 var prefixDash = prefix ? prefix + '-' : '';
23703 var rot = ele.pstyle(prefixDash + 'text-rotation');
23704 var rotStr = rot.strValue;
23705
23706 if (rotStr === 'none') {
23707 return 0;
23708 } else if (isEdge && rotStr === 'autorotate') {
23709 return rs.labelAutoAngle;
23710 } else if (rotStr === 'autorotate') {
23711 return 0;
23712 } else {
23713 return rot.pfValue;
23714 }
23715};
23716
23717BRp$6.calculateLabelAngles = function (ele) {
23718 var r = this;
23719 var isEdge = ele.isEdge();
23720 var _p = ele._private;
23721 var rs = _p.rscratch;
23722 rs.labelAngle = r.calculateLabelAngle(ele);
23723
23724 if (isEdge) {
23725 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
23726 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
23727 }
23728};
23729
23730var BRp$7 = {};
23731var TOO_SMALL_CUT_RECT = 28;
23732var warnedCutRect = false;
23733
23734BRp$7.getNodeShape = function (node) {
23735 var r = this;
23736 var shape = node.pstyle('shape').value;
23737
23738 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
23739 if (!warnedCutRect) {
23740 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
23741 warnedCutRect = true;
23742 }
23743
23744 return 'rectangle';
23745 }
23746
23747 if (node.isParent()) {
23748 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'cutrectangle' || shape === 'barrel') {
23749 return shape;
23750 } else {
23751 return 'rectangle';
23752 }
23753 }
23754
23755 if (shape === 'polygon') {
23756 var points = node.pstyle('shape-polygon-points').value;
23757 return r.nodeShapes.makePolygon(points).name;
23758 }
23759
23760 return shape;
23761};
23762
23763var BRp$8 = {};
23764
23765BRp$8.registerCalculationListeners = function () {
23766 var cy = this.cy;
23767 var elesToUpdate = cy.collection();
23768 var r = this;
23769
23770 var enqueue = function enqueue(eles) {
23771 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
23772 elesToUpdate.merge(eles);
23773
23774 if (dirtyStyleCaches) {
23775 for (var i = 0; i < eles.length; i++) {
23776 var ele = eles[i];
23777 var _p = ele._private;
23778 var rstyle = _p.rstyle;
23779 rstyle.clean = false;
23780 rstyle.cleanConnected = false;
23781 }
23782 }
23783 };
23784
23785 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
23786 var ele = e.target;
23787 enqueue(ele);
23788 }).on('style.* background.*', function onDirtyStyle(e) {
23789 var ele = e.target;
23790 enqueue(ele, false);
23791 });
23792
23793 var updateEleCalcs = function updateEleCalcs(willDraw) {
23794 if (willDraw) {
23795 var fns = r.onUpdateEleCalcsFns;
23796
23797 for (var i = 0; i < elesToUpdate.length; i++) {
23798 var ele = elesToUpdate[i];
23799 var rstyle = ele._private.rstyle;
23800
23801 if (ele.isNode() && !rstyle.cleanConnected) {
23802 enqueue(ele.connectedEdges());
23803 rstyle.cleanConnected = true;
23804 }
23805 }
23806
23807 if (fns) {
23808 for (var _i = 0; _i < fns.length; _i++) {
23809 var fn = fns[_i];
23810 fn(willDraw, elesToUpdate);
23811 }
23812 }
23813
23814 r.recalculateRenderedStyle(elesToUpdate);
23815 elesToUpdate = cy.collection();
23816 }
23817 };
23818
23819 r.flushRenderedStyleQueue = function () {
23820 updateEleCalcs(true);
23821 };
23822
23823 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
23824};
23825
23826BRp$8.onUpdateEleCalcs = function (fn) {
23827 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
23828 fns.push(fn);
23829};
23830
23831BRp$8.recalculateRenderedStyle = function (eles, useCache) {
23832 var isCleanConnected = function isCleanConnected(ele) {
23833 return ele._private.rstyle.cleanConnected;
23834 };
23835
23836 var edges = [];
23837 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
23838
23839 if (this.destroyed) {
23840 return;
23841 } // use cache by default for perf
23842
23843
23844 if (useCache === undefined) {
23845 useCache = true;
23846 }
23847
23848 for (var i = 0; i < eles.length; i++) {
23849 var ele = eles[i];
23850 var _p = ele._private;
23851 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
23852 // (and a request for recalc may come in between frames)
23853
23854 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
23855 rstyle.clean = false;
23856 } // only update if dirty and in graph
23857
23858
23859 if (useCache && rstyle.clean || ele.removed()) {
23860 continue;
23861 } // only update if not display: none
23862
23863
23864 if (ele.pstyle('display').value === 'none') {
23865 continue;
23866 }
23867
23868 if (_p.group === 'nodes') {
23869 nodes.push(ele);
23870 } else {
23871 // edges
23872 edges.push(ele);
23873 }
23874
23875 rstyle.clean = true;
23876 } // update node data from projections
23877
23878
23879 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
23880 var _ele = nodes[_i2];
23881 var _p2 = _ele._private;
23882 var _rstyle = _p2.rstyle;
23883
23884 var pos = _ele.position();
23885
23886 this.recalculateNodeLabelProjection(_ele);
23887 _rstyle.nodeX = pos.x;
23888 _rstyle.nodeY = pos.y;
23889 _rstyle.nodeW = _ele.pstyle('width').pfValue;
23890 _rstyle.nodeH = _ele.pstyle('height').pfValue;
23891 }
23892
23893 this.recalculateEdgeProjections(edges); // update edge data from projections
23894
23895 for (var _i3 = 0; _i3 < edges.length; _i3++) {
23896 var _ele2 = edges[_i3];
23897 var _p3 = _ele2._private;
23898 var _rstyle2 = _p3.rstyle;
23899 var rs = _p3.rscratch; // update rstyle positions
23900
23901 _rstyle2.srcX = rs.arrowStartX;
23902 _rstyle2.srcY = rs.arrowStartY;
23903 _rstyle2.tgtX = rs.arrowEndX;
23904 _rstyle2.tgtY = rs.arrowEndY;
23905 _rstyle2.midX = rs.midX;
23906 _rstyle2.midY = rs.midY;
23907 _rstyle2.labelAngle = rs.labelAngle;
23908 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
23909 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
23910 }
23911};
23912
23913var BRp$9 = {};
23914
23915BRp$9.updateCachedGrabbedEles = function () {
23916 var eles = this.cachedZSortedEles;
23917
23918 if (!eles) {
23919 // just let this be recalculated on the next z sort tick
23920 return;
23921 }
23922
23923 eles.drag = [];
23924 eles.nondrag = [];
23925 var grabTargets = [];
23926
23927 for (var i = 0; i < eles.length; i++) {
23928 var ele = eles[i];
23929 var rs = ele._private.rscratch;
23930
23931 if (ele.grabbed() && !ele.isParent()) {
23932 grabTargets.push(ele);
23933 } else if (rs.inDragLayer) {
23934 eles.drag.push(ele);
23935 } else {
23936 eles.nondrag.push(ele);
23937 }
23938 } // put the grab target nodes last so it's on top of its neighbourhood
23939
23940
23941 for (var i = 0; i < grabTargets.length; i++) {
23942 var ele = grabTargets[i];
23943 eles.drag.push(ele);
23944 }
23945};
23946
23947BRp$9.invalidateCachedZSortedEles = function () {
23948 this.cachedZSortedEles = null;
23949};
23950
23951BRp$9.getCachedZSortedEles = function (forceRecalc) {
23952 if (forceRecalc || !this.cachedZSortedEles) {
23953 var eles = this.cy.mutableElements().toArray();
23954 eles.sort(zIndexSort);
23955 eles.interactive = eles.filter(function (ele) {
23956 return ele.interactive();
23957 });
23958 this.cachedZSortedEles = eles;
23959 this.updateCachedGrabbedEles();
23960 } else {
23961 eles = this.cachedZSortedEles;
23962 }
23963
23964 return eles;
23965};
23966
23967var BRp$a = {};
23968[BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
23969 extend(BRp$a, props);
23970});
23971
23972var BRp$b = {};
23973
23974BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
23975 var r = this;
23976 var imageCache = r.imageCache = r.imageCache || {};
23977 var cache = imageCache[url];
23978
23979 if (cache) {
23980 if (!cache.image.complete) {
23981 cache.image.addEventListener('load', onLoad);
23982 }
23983
23984 return cache.image;
23985 } else {
23986 cache = imageCache[url] = imageCache[url] || {};
23987 var image = cache.image = new Image(); // eslint-disable-line no-undef
23988
23989 image.addEventListener('load', onLoad);
23990 image.addEventListener('error', function () {
23991 image.error = true;
23992 }); // #1582 safari doesn't load data uris with crossOrigin properly
23993 // https://bugs.webkit.org/show_bug.cgi?id=123978
23994
23995 var dataUriPrefix = 'data:';
23996 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
23997
23998 if (!isDataUri) {
23999 image.crossOrigin = crossOrigin; // prevent tainted canvas
24000 }
24001
24002 image.src = url;
24003 return image;
24004 }
24005};
24006
24007var BRp$c = {};
24008/* global document, window, ResizeObserver, MutationObserver */
24009
24010BRp$c.registerBinding = function (target, event, handler, useCapture) {
24011 // eslint-disable-line no-unused-vars
24012 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24013
24014 var b = this.binder(target);
24015 return b.on.apply(b, args);
24016};
24017
24018BRp$c.binder = function (tgt) {
24019 var r = this;
24020 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24021
24022 if (r.supportsPassiveEvents == null) {
24023 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24024 var supportsPassive = false;
24025
24026 try {
24027 var opts = Object.defineProperty({}, 'passive', {
24028 get: function get() {
24029 supportsPassive = true;
24030 return true;
24031 }
24032 });
24033 window.addEventListener('test', null, opts);
24034 } catch (err) {// not supported
24035 }
24036
24037 r.supportsPassiveEvents = supportsPassive;
24038 }
24039
24040 var on = function on(event, handler, useCapture) {
24041 var args = Array.prototype.slice.call(arguments);
24042
24043 if (tgtIsDom && r.supportsPassiveEvents) {
24044 // replace useCapture w/ opts obj
24045 args[2] = {
24046 capture: useCapture != null ? useCapture : false,
24047 passive: false,
24048 once: false
24049 };
24050 }
24051
24052 r.bindings.push({
24053 target: tgt,
24054 args: args
24055 });
24056 (tgt.addEventListener || tgt.on).apply(tgt, args);
24057 return this;
24058 };
24059
24060 return {
24061 on: on,
24062 addEventListener: on,
24063 addListener: on,
24064 bind: on
24065 };
24066};
24067
24068BRp$c.nodeIsDraggable = function (node) {
24069 return node && node.isNode() && !node.locked() && node.grabbable();
24070};
24071
24072BRp$c.nodeIsGrabbable = function (node) {
24073 return this.nodeIsDraggable(node) && node.interactive();
24074};
24075
24076BRp$c.load = function () {
24077 var r = this;
24078
24079 var isSelected = function isSelected(ele) {
24080 return ele.selected();
24081 };
24082
24083 var triggerEvents = function triggerEvents(target, names, e, position) {
24084 if (target == null) {
24085 target = r.cy;
24086 }
24087
24088 for (var i = 0; i < names.length; i++) {
24089 var name = names[i];
24090 target.emit({
24091 originalEvent: e,
24092 type: name,
24093 position: position
24094 });
24095 }
24096 };
24097
24098 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24099 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24100 };
24101
24102 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24103 var allowPassthrough = true;
24104
24105 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24106 // a grabbable compound node below the ele => no passthrough panning
24107 for (var i = 0; downs && i < downs.length; i++) {
24108 var down = downs[i];
24109
24110 if (down.isNode() && down.isParent()) {
24111 allowPassthrough = false;
24112 break;
24113 }
24114 }
24115 } else {
24116 allowPassthrough = true;
24117 }
24118
24119 return allowPassthrough;
24120 };
24121
24122 var setGrabbed = function setGrabbed(ele) {
24123 ele[0]._private.grabbed = true;
24124 };
24125
24126 var setFreed = function setFreed(ele) {
24127 ele[0]._private.grabbed = false;
24128 };
24129
24130 var setInDragLayer = function setInDragLayer(ele) {
24131 ele[0]._private.rscratch.inDragLayer = true;
24132 };
24133
24134 var setOutDragLayer = function setOutDragLayer(ele) {
24135 ele[0]._private.rscratch.inDragLayer = false;
24136 };
24137
24138 var setGrabTarget = function setGrabTarget(ele) {
24139 ele[0]._private.rscratch.isGrabTarget = true;
24140 };
24141
24142 var removeGrabTarget = function removeGrabTarget(ele) {
24143 ele[0]._private.rscratch.isGrabTarget = false;
24144 };
24145
24146 var addToDragList = function addToDragList(ele, opts) {
24147 var list = opts.addToList;
24148 var listHasEle = list.has(ele);
24149
24150 if (!listHasEle) {
24151 list.merge(ele);
24152 setGrabbed(ele);
24153 }
24154 }; // helper function to determine which child nodes and inner edges
24155 // of a compound node to be dragged as well as the grabbed and selected nodes
24156
24157
24158 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24159 if (!node.cy().hasCompoundNodes()) {
24160 return;
24161 }
24162
24163 if (opts.inDragLayer == null && opts.addToList == null) {
24164 return;
24165 } // nothing to do
24166
24167
24168 var innerNodes = node.descendants();
24169
24170 if (opts.inDragLayer) {
24171 innerNodes.forEach(setInDragLayer);
24172 innerNodes.connectedEdges().forEach(setInDragLayer);
24173 }
24174
24175 if (opts.addToList) {
24176 opts.addToList.unmerge(innerNodes);
24177 }
24178 }; // adds the given nodes and its neighbourhood to the drag layer
24179
24180
24181 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24182 opts = opts || {};
24183 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24184
24185 if (opts.inDragLayer) {
24186 nodes.forEach(setInDragLayer);
24187 nodes.neighborhood().stdFilter(function (ele) {
24188 return !hasCompoundNodes || ele.isEdge();
24189 }).forEach(setInDragLayer);
24190 }
24191
24192 if (opts.addToList) {
24193 nodes.forEach(function (ele) {
24194 addToDragList(ele, opts);
24195 });
24196 }
24197
24198 addDescendantsToDrag(nodes, opts); // always add to drag
24199 // also add nodes and edges related to the topmost ancestor
24200
24201 updateAncestorsInDragLayer(nodes, {
24202 inDragLayer: opts.inDragLayer
24203 });
24204 r.updateCachedGrabbedEles();
24205 };
24206
24207 var addNodeToDrag = addNodesToDrag;
24208
24209 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24210 if (!grabbedEles) {
24211 return;
24212 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24213
24214
24215 r.getCachedZSortedEles().forEach(function (ele) {
24216 setFreed(ele);
24217 setOutDragLayer(ele);
24218 removeGrabTarget(ele);
24219 });
24220 r.updateCachedGrabbedEles();
24221 }; // helper function to determine which ancestor nodes and edges should go
24222 // to the drag layer (or should be removed from drag layer).
24223
24224
24225 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24226 if (opts.inDragLayer == null && opts.addToList == null) {
24227 return;
24228 } // nothing to do
24229
24230
24231 if (!node.cy().hasCompoundNodes()) {
24232 return;
24233 } // find top-level parent
24234
24235
24236 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
24237
24238 if (parent.same(node)) {
24239 return;
24240 }
24241
24242 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24243 var edges = nodes.connectedEdges();
24244
24245 if (opts.inDragLayer) {
24246 edges.forEach(setInDragLayer);
24247 nodes.forEach(setInDragLayer);
24248 }
24249
24250 if (opts.addToList) {
24251 nodes.forEach(function (ele) {
24252 addToDragList(ele, opts);
24253 });
24254 }
24255 };
24256
24257 var blurActiveDomElement = function blurActiveDomElement() {
24258 if (document.activeElement != null && document.activeElement.blur != null) {
24259 document.activeElement.blur();
24260 }
24261 };
24262
24263 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24264 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
24265
24266 if (haveMutationsApi) {
24267 r.removeObserver = new MutationObserver(function (mutns) {
24268 // eslint-disable-line no-undef
24269 for (var i = 0; i < mutns.length; i++) {
24270 var mutn = mutns[i];
24271 var rNodes = mutn.removedNodes;
24272
24273 if (rNodes) {
24274 for (var j = 0; j < rNodes.length; j++) {
24275 var rNode = rNodes[j];
24276
24277 if (rNode === r.container) {
24278 r.destroy();
24279 break;
24280 }
24281 }
24282 }
24283 }
24284 });
24285
24286 if (r.container.parentNode) {
24287 r.removeObserver.observe(r.container.parentNode, {
24288 childList: true
24289 });
24290 }
24291 } else {
24292 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24293 // eslint-disable-line no-unused-vars
24294 r.destroy();
24295 });
24296 }
24297
24298 var onResize = util(function () {
24299 r.cy.resize();
24300 }, 100);
24301
24302 if (haveMutationsApi) {
24303 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24304
24305 r.styleObserver.observe(r.container, {
24306 attributes: true
24307 });
24308 } // auto resize
24309
24310
24311 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
24312
24313 if (haveResizeObserverApi) {
24314 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24315
24316 r.resizeObserver.observe(r.container);
24317 }
24318
24319 var forEachUp = function forEachUp(domEle, fn) {
24320 while (domEle != null) {
24321 fn(domEle);
24322 domEle = domEle.parentNode;
24323 }
24324 };
24325
24326 var invalidateCoords = function invalidateCoords() {
24327 r.invalidateContainerClientCoordsCache();
24328 };
24329
24330 forEachUp(r.container, function (domEle) {
24331 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24332 r.registerBinding(domEle, 'animationend', invalidateCoords);
24333 r.registerBinding(domEle, 'scroll', invalidateCoords);
24334 }); // stop right click menu from appearing on cy
24335
24336 r.registerBinding(r.container, 'contextmenu', function (e) {
24337 e.preventDefault();
24338 });
24339
24340 var inBoxSelection = function inBoxSelection() {
24341 return r.selection[4] !== 0;
24342 };
24343
24344 var eventInContainer = function eventInContainer(e) {
24345 // save cycles if mouse events aren't to be captured
24346 var containerPageCoords = r.findContainerClientCoords();
24347 var x = containerPageCoords[0];
24348 var y = containerPageCoords[1];
24349 var width = containerPageCoords[2];
24350 var height = containerPageCoords[3];
24351 var positions = e.touches ? e.touches : [e];
24352 var atLeastOnePosInside = false;
24353
24354 for (var i = 0; i < positions.length; i++) {
24355 var p = positions[i];
24356
24357 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24358 atLeastOnePosInside = true;
24359 break;
24360 }
24361 }
24362
24363 if (!atLeastOnePosInside) {
24364 return false;
24365 }
24366
24367 var container = r.container;
24368 var target = e.target;
24369 var tParent = target.parentNode;
24370 var containerIsTarget = false;
24371
24372 while (tParent) {
24373 if (tParent === container) {
24374 containerIsTarget = true;
24375 break;
24376 }
24377
24378 tParent = tParent.parentNode;
24379 }
24380
24381 if (!containerIsTarget) {
24382 return false;
24383 } // if target is outisde cy container, then this event is not for us
24384
24385
24386 return true;
24387 }; // Primary key
24388
24389
24390 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24391 if (!eventInContainer(e)) {
24392 return;
24393 }
24394
24395 e.preventDefault();
24396 blurActiveDomElement();
24397 r.hoverData.capture = true;
24398 r.hoverData.which = e.which;
24399 var cy = r.cy;
24400 var gpos = [e.clientX, e.clientY];
24401 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24402 var select = r.selection;
24403 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24404 var near = nears[0];
24405 var draggedElements = r.dragData.possibleDragElements;
24406 r.hoverData.mdownPos = pos;
24407 r.hoverData.mdownGPos = gpos;
24408
24409 var checkForTaphold = function checkForTaphold() {
24410 r.hoverData.tapholdCancelled = false;
24411 clearTimeout(r.hoverData.tapholdTimeout);
24412 r.hoverData.tapholdTimeout = setTimeout(function () {
24413 if (r.hoverData.tapholdCancelled) {
24414 return;
24415 } else {
24416 var ele = r.hoverData.down;
24417
24418 if (ele) {
24419 ele.emit({
24420 originalEvent: e,
24421 type: 'taphold',
24422 position: {
24423 x: pos[0],
24424 y: pos[1]
24425 }
24426 });
24427 } else {
24428 cy.emit({
24429 originalEvent: e,
24430 type: 'taphold',
24431 position: {
24432 x: pos[0],
24433 y: pos[1]
24434 }
24435 });
24436 }
24437 }
24438 }, r.tapholdDuration);
24439 }; // Right click button
24440
24441
24442 if (e.which == 3) {
24443 r.hoverData.cxtStarted = true;
24444 var cxtEvt = {
24445 originalEvent: e,
24446 type: 'cxttapstart',
24447 position: {
24448 x: pos[0],
24449 y: pos[1]
24450 }
24451 };
24452
24453 if (near) {
24454 near.activate();
24455 near.emit(cxtEvt);
24456 r.hoverData.down = near;
24457 } else {
24458 cy.emit(cxtEvt);
24459 }
24460
24461 r.hoverData.downTime = new Date().getTime();
24462 r.hoverData.cxtDragged = false; // Primary button
24463 } else if (e.which == 1) {
24464 if (near) {
24465 near.activate();
24466 } // Element dragging
24467
24468
24469 {
24470 // If something is under the cursor and it is draggable, prepare to grab it
24471 if (near != null) {
24472 if (r.nodeIsGrabbable(near)) {
24473 var makeEvent = function makeEvent(type) {
24474 return {
24475 originalEvent: e,
24476 type: type,
24477 position: {
24478 x: pos[0],
24479 y: pos[1]
24480 }
24481 };
24482 };
24483
24484 var triggerGrab = function triggerGrab(ele) {
24485 ele.emit(makeEvent('grab'));
24486 };
24487
24488 setGrabTarget(near);
24489
24490 if (!near.selected()) {
24491 draggedElements = r.dragData.possibleDragElements = cy.collection();
24492 addNodeToDrag(near, {
24493 addToList: draggedElements
24494 });
24495 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24496 } else {
24497 draggedElements = r.dragData.possibleDragElements = cy.collection();
24498 var selectedNodes = cy.$(function (ele) {
24499 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24500 });
24501 addNodesToDrag(selectedNodes, {
24502 addToList: draggedElements
24503 });
24504 near.emit(makeEvent('grabon'));
24505 selectedNodes.forEach(triggerGrab);
24506 }
24507
24508 r.redrawHint('eles', true);
24509 r.redrawHint('drag', true);
24510 }
24511 }
24512
24513 r.hoverData.down = near;
24514 r.hoverData.downs = nears;
24515 r.hoverData.downTime = new Date().getTime();
24516 }
24517 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24518 x: pos[0],
24519 y: pos[1]
24520 });
24521
24522 if (near == null) {
24523 select[4] = 1;
24524 r.data.bgActivePosistion = {
24525 x: pos[0],
24526 y: pos[1]
24527 };
24528 r.redrawHint('select', true);
24529 r.redraw();
24530 } else if (near.pannable()) {
24531 select[4] = 1; // for future pan
24532 }
24533
24534 checkForTaphold();
24535 } // Initialize selection box coordinates
24536
24537
24538 select[0] = select[2] = pos[0];
24539 select[1] = select[3] = pos[1];
24540 }, false);
24541 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
24542 // eslint-disable-line no-undef
24543 var capture = r.hoverData.capture;
24544
24545 if (!capture && !eventInContainer(e)) {
24546 return;
24547 }
24548
24549 var preventDefault = false;
24550 var cy = r.cy;
24551 var zoom = cy.zoom();
24552 var gpos = [e.clientX, e.clientY];
24553 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24554 var mdownPos = r.hoverData.mdownPos;
24555 var mdownGPos = r.hoverData.mdownGPos;
24556 var select = r.selection;
24557 var near = null;
24558
24559 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24560 near = r.findNearestElement(pos[0], pos[1], true, false);
24561 }
24562
24563 var last = r.hoverData.last;
24564 var down = r.hoverData.down;
24565 var disp = [pos[0] - select[2], pos[1] - select[3]];
24566 var draggedElements = r.dragData.possibleDragElements;
24567 var isOverThresholdDrag;
24568
24569 if (mdownGPos) {
24570 var dx = gpos[0] - mdownGPos[0];
24571 var dx2 = dx * dx;
24572 var dy = gpos[1] - mdownGPos[1];
24573 var dy2 = dy * dy;
24574 var dist2 = dx2 + dy2;
24575 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24576 }
24577
24578 var multSelKeyDown = isMultSelKeyDown(e);
24579
24580 if (isOverThresholdDrag) {
24581 r.hoverData.tapholdCancelled = true;
24582 }
24583
24584 var updateDragDelta = function updateDragDelta() {
24585 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24586
24587 if (dragDelta.length === 0) {
24588 dragDelta.push(disp[0]);
24589 dragDelta.push(disp[1]);
24590 } else {
24591 dragDelta[0] += disp[0];
24592 dragDelta[1] += disp[1];
24593 }
24594 };
24595
24596 preventDefault = true;
24597 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24598 x: pos[0],
24599 y: pos[1]
24600 });
24601
24602 var goIntoBoxMode = function goIntoBoxMode() {
24603 r.data.bgActivePosistion = undefined;
24604
24605 if (!r.hoverData.selecting) {
24606 cy.emit({
24607 originalEvent: e,
24608 type: 'boxstart',
24609 position: {
24610 x: pos[0],
24611 y: pos[1]
24612 }
24613 });
24614 }
24615
24616 select[4] = 1;
24617 r.hoverData.selecting = true;
24618 r.redrawHint('select', true);
24619 r.redraw();
24620 }; // trigger context drag if rmouse down
24621
24622
24623 if (r.hoverData.which === 3) {
24624 // but only if over threshold
24625 if (isOverThresholdDrag) {
24626 var cxtEvt = {
24627 originalEvent: e,
24628 type: 'cxtdrag',
24629 position: {
24630 x: pos[0],
24631 y: pos[1]
24632 }
24633 };
24634
24635 if (down) {
24636 down.emit(cxtEvt);
24637 } else {
24638 cy.emit(cxtEvt);
24639 }
24640
24641 r.hoverData.cxtDragged = true;
24642
24643 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24644 if (r.hoverData.cxtOver) {
24645 r.hoverData.cxtOver.emit({
24646 originalEvent: e,
24647 type: 'cxtdragout',
24648 position: {
24649 x: pos[0],
24650 y: pos[1]
24651 }
24652 });
24653 }
24654
24655 r.hoverData.cxtOver = near;
24656
24657 if (near) {
24658 near.emit({
24659 originalEvent: e,
24660 type: 'cxtdragover',
24661 position: {
24662 x: pos[0],
24663 y: pos[1]
24664 }
24665 });
24666 }
24667 }
24668 } // Check if we are drag panning the entire graph
24669
24670 } else if (r.hoverData.dragging) {
24671 preventDefault = true;
24672
24673 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24674 var deltaP;
24675
24676 if (r.hoverData.justStartedPan) {
24677 var mdPos = r.hoverData.mdownPos;
24678 deltaP = {
24679 x: (pos[0] - mdPos[0]) * zoom,
24680 y: (pos[1] - mdPos[1]) * zoom
24681 };
24682 r.hoverData.justStartedPan = false;
24683 } else {
24684 deltaP = {
24685 x: disp[0] * zoom,
24686 y: disp[1] * zoom
24687 };
24688 }
24689
24690 cy.panBy(deltaP);
24691 r.hoverData.dragged = true;
24692 } // Needs reproject due to pan changing viewport
24693
24694
24695 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24696 } else if (select[4] == 1 && (down == null || down.pannable())) {
24697 if (isOverThresholdDrag) {
24698 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24699 goIntoBoxMode();
24700 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24701 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24702
24703 if (allowPassthrough) {
24704 r.hoverData.dragging = true;
24705 r.hoverData.justStartedPan = true;
24706 select[4] = 0;
24707 r.data.bgActivePosistion = array2point(mdownPos);
24708 r.redrawHint('select', true);
24709 r.redraw();
24710 }
24711 }
24712
24713 if (down && down.pannable() && down.active()) {
24714 down.unactivate();
24715 }
24716 }
24717 } else {
24718 if (down && down.pannable() && down.active()) {
24719 down.unactivate();
24720 }
24721
24722 if ((!down || !down.grabbed()) && near != last) {
24723 if (last) {
24724 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24725 x: pos[0],
24726 y: pos[1]
24727 });
24728 }
24729
24730 if (near) {
24731 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24732 x: pos[0],
24733 y: pos[1]
24734 });
24735 }
24736
24737 r.hoverData.last = near;
24738 }
24739
24740 if (down) {
24741 if (isOverThresholdDrag) {
24742 // then we can take action
24743 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24744 // then selection overrides
24745 if (down && down.grabbed()) {
24746 freeDraggedElements(draggedElements);
24747 down.emit('freeon');
24748 draggedElements.emit('free');
24749
24750 if (r.dragData.didDrag) {
24751 down.emit('dragfreeon');
24752 draggedElements.emit('dragfree');
24753 }
24754 }
24755
24756 goIntoBoxMode();
24757 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24758 // drag node
24759 var justStartedDrag = !r.dragData.didDrag;
24760
24761 if (justStartedDrag) {
24762 r.redrawHint('eles', true);
24763 }
24764
24765 r.dragData.didDrag = true; // indicate that we actually did drag the node
24766
24767 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
24768
24769 if (!r.hoverData.draggingEles) {
24770 addNodesToDrag(draggedElements, {
24771 inDragLayer: true
24772 });
24773 }
24774
24775 var totalShift = {
24776 x: 0,
24777 y: 0
24778 };
24779
24780 if (number(disp[0]) && number(disp[1])) {
24781 totalShift.x += disp[0];
24782 totalShift.y += disp[1];
24783
24784 if (justStartedDrag) {
24785 var dragDelta = r.hoverData.dragDelta;
24786
24787 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
24788 totalShift.x += dragDelta[0];
24789 totalShift.y += dragDelta[1];
24790 }
24791 }
24792 }
24793
24794 for (var i = 0; i < draggedElements.length; i++) {
24795 var dEle = draggedElements[i];
24796
24797 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
24798 toTrigger.merge(dEle);
24799 }
24800 }
24801
24802 r.hoverData.draggingEles = true;
24803 toTrigger.silentShift(totalShift).emit('position drag');
24804 r.redrawHint('drag', true);
24805 r.redraw();
24806 }
24807 } else {
24808 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
24809 updateDragDelta();
24810 }
24811 } // prevent the dragging from triggering text selection on the page
24812
24813
24814 preventDefault = true;
24815 }
24816
24817 select[2] = pos[0];
24818 select[3] = pos[1];
24819
24820 if (preventDefault) {
24821 if (e.stopPropagation) e.stopPropagation();
24822 if (e.preventDefault) e.preventDefault();
24823 return false;
24824 }
24825 }, false);
24826 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
24827 // eslint-disable-line no-undef
24828 var capture = r.hoverData.capture;
24829
24830 if (!capture) {
24831 return;
24832 }
24833
24834 r.hoverData.capture = false;
24835 var cy = r.cy;
24836 var pos = r.projectIntoViewport(e.clientX, e.clientY);
24837 var select = r.selection;
24838 var near = r.findNearestElement(pos[0], pos[1], true, false);
24839 var draggedElements = r.dragData.possibleDragElements;
24840 var down = r.hoverData.down;
24841 var multSelKeyDown = isMultSelKeyDown(e);
24842
24843 if (r.data.bgActivePosistion) {
24844 r.redrawHint('select', true);
24845 r.redraw();
24846 }
24847
24848 r.hoverData.tapholdCancelled = true;
24849 r.data.bgActivePosistion = undefined; // not active bg now
24850
24851 if (down) {
24852 down.unactivate();
24853 }
24854
24855 if (r.hoverData.which === 3) {
24856 var cxtEvt = {
24857 originalEvent: e,
24858 type: 'cxttapend',
24859 position: {
24860 x: pos[0],
24861 y: pos[1]
24862 }
24863 };
24864
24865 if (down) {
24866 down.emit(cxtEvt);
24867 } else {
24868 cy.emit(cxtEvt);
24869 }
24870
24871 if (!r.hoverData.cxtDragged) {
24872 var cxtTap = {
24873 originalEvent: e,
24874 type: 'cxttap',
24875 position: {
24876 x: pos[0],
24877 y: pos[1]
24878 }
24879 };
24880
24881 if (down) {
24882 down.emit(cxtTap);
24883 } else {
24884 cy.emit(cxtTap);
24885 }
24886 }
24887
24888 r.hoverData.cxtDragged = false;
24889 r.hoverData.which = null;
24890 } else if (r.hoverData.which === 1) {
24891 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
24892 x: pos[0],
24893 y: pos[1]
24894 });
24895
24896 if (!r.dragData.didDrag // didn't move a node around
24897 && !r.hoverData.dragged // didn't pan
24898 && !r.hoverData.selecting // not box selection
24899 && !r.hoverData.isOverThresholdDrag // didn't move too much
24900 ) {
24901 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
24902 x: pos[0],
24903 y: pos[1]
24904 });
24905 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
24906
24907
24908 if (down == null && // not mousedown on node
24909 !r.dragData.didDrag // didn't move the node around
24910 && !r.hoverData.selecting // not box selection
24911 && !r.hoverData.dragged // didn't pan
24912 && !isMultSelKeyDown(e)) {
24913 cy.$(isSelected).unselect(['tapunselect']);
24914
24915 if (draggedElements.length > 0) {
24916 r.redrawHint('eles', true);
24917 }
24918
24919 r.dragData.possibleDragElements = draggedElements = cy.collection();
24920 } // Single selection
24921
24922
24923 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
24924 if (near != null && near._private.selectable) {
24925 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
24926 if (near.selected()) {
24927 near.unselect(['tapunselect']);
24928 } else {
24929 near.select(['tapselect']);
24930 }
24931 } else {
24932 if (!multSelKeyDown) {
24933 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
24934 near.select(['tapselect']);
24935 }
24936 }
24937
24938 r.redrawHint('eles', true);
24939 }
24940 }
24941
24942 if (r.hoverData.selecting) {
24943 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
24944 r.redrawHint('select', true);
24945
24946 if (box.length > 0) {
24947 r.redrawHint('eles', true);
24948 }
24949
24950 cy.emit({
24951 type: 'boxend',
24952 originalEvent: e,
24953 position: {
24954 x: pos[0],
24955 y: pos[1]
24956 }
24957 });
24958
24959 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
24960 return ele.selectable() && !ele.selected();
24961 };
24962
24963 if (cy.selectionType() === 'additive') {
24964 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
24965 } else {
24966 if (!multSelKeyDown) {
24967 cy.$(isSelected).unmerge(box).unselect();
24968 }
24969
24970 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
24971 } // always need redraw in case eles unselectable
24972
24973
24974 r.redraw();
24975 } // Cancel drag pan
24976
24977
24978 if (r.hoverData.dragging) {
24979 r.hoverData.dragging = false;
24980 r.redrawHint('select', true);
24981 r.redrawHint('eles', true);
24982 r.redraw();
24983 }
24984
24985 if (!select[4]) {
24986 r.redrawHint('drag', true);
24987 r.redrawHint('eles', true);
24988 var downWasGrabbed = down && down.grabbed();
24989 freeDraggedElements(draggedElements);
24990
24991 if (downWasGrabbed) {
24992 down.emit('freeon');
24993 draggedElements.emit('free');
24994
24995 if (r.dragData.didDrag) {
24996 down.emit('dragfreeon');
24997 draggedElements.emit('dragfree');
24998 }
24999 }
25000 }
25001 } // else not right mouse
25002
25003
25004 select[4] = 0;
25005 r.hoverData.down = null;
25006 r.hoverData.cxtStarted = false;
25007 r.hoverData.draggingEles = false;
25008 r.hoverData.selecting = false;
25009 r.hoverData.isOverThresholdDrag = false;
25010 r.dragData.didDrag = false;
25011 r.hoverData.dragged = false;
25012 r.hoverData.dragDelta = [];
25013 r.hoverData.mdownPos = null;
25014 r.hoverData.mdownGPos = null;
25015 }, false);
25016
25017 var wheelHandler = function wheelHandler(e) {
25018 if (r.scrollingPage) {
25019 return;
25020 } // while scrolling, ignore wheel-to-zoom
25021
25022
25023 var cy = r.cy;
25024 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25025 var rpos = [pos[0] * cy.zoom() + cy.pan().x, pos[1] * cy.zoom() + cy.pan().y];
25026
25027 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25028 // if pan dragging or cxt dragging, wheel movements make no zoom
25029 e.preventDefault();
25030 return;
25031 }
25032
25033 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25034 e.preventDefault();
25035 r.data.wheelZooming = true;
25036 clearTimeout(r.data.wheelTimeout);
25037 r.data.wheelTimeout = setTimeout(function () {
25038 r.data.wheelZooming = false;
25039 r.redrawHint('eles', true);
25040 r.redraw();
25041 }, 150);
25042 var diff;
25043
25044 if (e.deltaY != null) {
25045 diff = e.deltaY / -250;
25046 } else if (e.wheelDeltaY != null) {
25047 diff = e.wheelDeltaY / 1000;
25048 } else {
25049 diff = e.wheelDelta / 1000;
25050 }
25051
25052 diff = diff * r.wheelSensitivity;
25053 var needsWheelFix = e.deltaMode === 1;
25054
25055 if (needsWheelFix) {
25056 // fixes slow wheel events on ff/linux and ff/windows
25057 diff *= 33;
25058 }
25059
25060 cy.zoom({
25061 level: cy.zoom() * Math.pow(10, diff),
25062 renderedPosition: {
25063 x: rpos[0],
25064 y: rpos[1]
25065 }
25066 });
25067 }
25068 }; // Functions to help with whether mouse wheel should trigger zooming
25069 // --
25070
25071
25072 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25073 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25074 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25075 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25076
25077 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25078 // eslint-disable-line no-unused-vars
25079 r.scrollingPage = true;
25080 clearTimeout(r.scrollingPageTimeout);
25081 r.scrollingPageTimeout = setTimeout(function () {
25082 r.scrollingPage = false;
25083 }, 250);
25084 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25085 // Handle mouseout on Cytoscape container
25086
25087 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25088 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25089 r.cy.emit({
25090 originalEvent: e,
25091 type: 'mouseout',
25092 position: {
25093 x: pos[0],
25094 y: pos[1]
25095 }
25096 });
25097 }, false);
25098 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25099 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25100 r.cy.emit({
25101 originalEvent: e,
25102 type: 'mouseover',
25103 position: {
25104 x: pos[0],
25105 y: pos[1]
25106 }
25107 });
25108 }, false);
25109 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25110
25111 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25112
25113 var center1, modelCenter1; // center point on start pinch to zoom
25114
25115 var offsetLeft, offsetTop;
25116 var containerWidth, containerHeight;
25117 var twoFingersStartInside;
25118
25119 var distance = function distance(x1, y1, x2, y2) {
25120 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25121 };
25122
25123 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25124 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25125 };
25126
25127 var touchstartHandler;
25128 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25129 if (!eventInContainer(e)) {
25130 return;
25131 }
25132
25133 blurActiveDomElement();
25134 r.touchData.capture = true;
25135 r.data.bgActivePosistion = undefined;
25136 var cy = r.cy;
25137 var now = r.touchData.now;
25138 var earlier = r.touchData.earlier;
25139
25140 if (e.touches[0]) {
25141 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25142 now[0] = pos[0];
25143 now[1] = pos[1];
25144 }
25145
25146 if (e.touches[1]) {
25147 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25148 now[2] = pos[0];
25149 now[3] = pos[1];
25150 }
25151
25152 if (e.touches[2]) {
25153 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25154 now[4] = pos[0];
25155 now[5] = pos[1];
25156 } // record starting points for pinch-to-zoom
25157
25158
25159 if (e.touches[1]) {
25160 r.touchData.singleTouchMoved = true;
25161 freeDraggedElements(r.dragData.touchDragEles);
25162 var offsets = r.findContainerClientCoords();
25163 offsetLeft = offsets[0];
25164 offsetTop = offsets[1];
25165 containerWidth = offsets[2];
25166 containerHeight = offsets[3];
25167 f1x1 = e.touches[0].clientX - offsetLeft;
25168 f1y1 = e.touches[0].clientY - offsetTop;
25169 f2x1 = e.touches[1].clientX - offsetLeft;
25170 f2y1 = e.touches[1].clientY - offsetTop;
25171 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25172 var pan = cy.pan();
25173 var zoom = cy.zoom();
25174 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25175 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25176 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25177 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25178
25179 var cxtDistThreshold = 200;
25180 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25181
25182 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25183 var near1 = r.findNearestElement(now[0], now[1], true, true);
25184 var near2 = r.findNearestElement(now[2], now[3], true, true);
25185
25186 if (near1 && near1.isNode()) {
25187 near1.activate().emit({
25188 originalEvent: e,
25189 type: 'cxttapstart',
25190 position: {
25191 x: now[0],
25192 y: now[1]
25193 }
25194 });
25195 r.touchData.start = near1;
25196 } else if (near2 && near2.isNode()) {
25197 near2.activate().emit({
25198 originalEvent: e,
25199 type: 'cxttapstart',
25200 position: {
25201 x: now[0],
25202 y: now[1]
25203 }
25204 });
25205 r.touchData.start = near2;
25206 } else {
25207 cy.emit({
25208 originalEvent: e,
25209 type: 'cxttapstart',
25210 position: {
25211 x: now[0],
25212 y: now[1]
25213 }
25214 });
25215 }
25216
25217 if (r.touchData.start) {
25218 r.touchData.start._private.grabbed = false;
25219 }
25220
25221 r.touchData.cxt = true;
25222 r.touchData.cxtDragged = false;
25223 r.data.bgActivePosistion = undefined;
25224 r.redraw();
25225 return;
25226 }
25227 }
25228
25229 if (e.touches[2]) {
25230 // ignore
25231 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25232 if (cy.boxSelectionEnabled()) {
25233 e.preventDefault();
25234 }
25235 } else if (e.touches[1]) ; else if (e.touches[0]) {
25236 var nears = r.findNearestElements(now[0], now[1], true, true);
25237 var near = nears[0];
25238
25239 if (near != null) {
25240 near.activate();
25241 r.touchData.start = near;
25242 r.touchData.starts = nears;
25243
25244 if (r.nodeIsGrabbable(near)) {
25245 var draggedEles = r.dragData.touchDragEles = cy.collection();
25246 var selectedNodes = null;
25247 r.redrawHint('eles', true);
25248 r.redrawHint('drag', true);
25249
25250 if (near.selected()) {
25251 // reset drag elements, since near will be added again
25252 selectedNodes = cy.$(function (ele) {
25253 return ele.selected() && r.nodeIsGrabbable(ele);
25254 });
25255 addNodesToDrag(selectedNodes, {
25256 addToList: draggedEles
25257 });
25258 } else {
25259 addNodeToDrag(near, {
25260 addToList: draggedEles
25261 });
25262 }
25263
25264 setGrabTarget(near);
25265
25266 var makeEvent = function makeEvent(type) {
25267 return {
25268 originalEvent: e,
25269 type: type,
25270 position: {
25271 x: now[0],
25272 y: now[1]
25273 }
25274 };
25275 };
25276
25277 near.emit(makeEvent('grabon'));
25278
25279 if (selectedNodes) {
25280 selectedNodes.forEach(function (n) {
25281 n.emit(makeEvent('grab'));
25282 });
25283 } else {
25284 near.emit(makeEvent('grab'));
25285 }
25286 }
25287 }
25288
25289 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25290 x: now[0],
25291 y: now[1]
25292 });
25293
25294 if (near == null) {
25295 r.data.bgActivePosistion = {
25296 x: pos[0],
25297 y: pos[1]
25298 };
25299 r.redrawHint('select', true);
25300 r.redraw();
25301 } // Tap, taphold
25302 // -----
25303
25304
25305 r.touchData.singleTouchMoved = false;
25306 r.touchData.singleTouchStartTime = +new Date();
25307 clearTimeout(r.touchData.tapholdTimeout);
25308 r.touchData.tapholdTimeout = setTimeout(function () {
25309 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25310 && !r.touchData.selecting // box selection shouldn't allow taphold through
25311 ) {
25312 triggerEvents(r.touchData.start, ['taphold'], e, {
25313 x: now[0],
25314 y: now[1]
25315 });
25316 }
25317 }, r.tapholdDuration);
25318 }
25319
25320 if (e.touches.length >= 1) {
25321 var sPos = r.touchData.startPosition = [];
25322
25323 for (var i = 0; i < now.length; i++) {
25324 sPos[i] = earlier[i] = now[i];
25325 }
25326
25327 var touch0 = e.touches[0];
25328 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25329 }
25330 }, false);
25331 var touchmoveHandler;
25332 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25333 // eslint-disable-line no-undef
25334 var capture = r.touchData.capture;
25335
25336 if (!capture && !eventInContainer(e)) {
25337 return;
25338 }
25339
25340 var select = r.selection;
25341 var cy = r.cy;
25342 var now = r.touchData.now;
25343 var earlier = r.touchData.earlier;
25344 var zoom = cy.zoom();
25345
25346 if (e.touches[0]) {
25347 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25348 now[0] = pos[0];
25349 now[1] = pos[1];
25350 }
25351
25352 if (e.touches[1]) {
25353 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25354 now[2] = pos[0];
25355 now[3] = pos[1];
25356 }
25357
25358 if (e.touches[2]) {
25359 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25360 now[4] = pos[0];
25361 now[5] = pos[1];
25362 }
25363
25364 var startGPos = r.touchData.startGPosition;
25365 var isOverThresholdDrag;
25366
25367 if (capture && e.touches[0] && startGPos) {
25368 var disp = [];
25369
25370 for (var j = 0; j < now.length; j++) {
25371 disp[j] = now[j] - earlier[j];
25372 }
25373
25374 var dx = e.touches[0].clientX - startGPos[0];
25375 var dx2 = dx * dx;
25376 var dy = e.touches[0].clientY - startGPos[1];
25377 var dy2 = dy * dy;
25378 var dist2 = dx2 + dy2;
25379 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25380 } // context swipe cancelling
25381
25382
25383 if (capture && r.touchData.cxt) {
25384 e.preventDefault();
25385 var f1x2 = e.touches[0].clientX - offsetLeft,
25386 f1y2 = e.touches[0].clientY - offsetTop;
25387 var f2x2 = e.touches[1].clientX - offsetLeft,
25388 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25389
25390 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25391 var factorSq = distance2Sq / distance1Sq;
25392 var distThreshold = 150;
25393 var distThresholdSq = distThreshold * distThreshold;
25394 var factorThreshold = 1.5;
25395 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25396
25397 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25398 r.touchData.cxt = false;
25399 r.data.bgActivePosistion = undefined;
25400 r.redrawHint('select', true);
25401 var cxtEvt = {
25402 originalEvent: e,
25403 type: 'cxttapend',
25404 position: {
25405 x: now[0],
25406 y: now[1]
25407 }
25408 };
25409
25410 if (r.touchData.start) {
25411 r.touchData.start.unactivate().emit(cxtEvt);
25412 r.touchData.start = null;
25413 } else {
25414 cy.emit(cxtEvt);
25415 }
25416 }
25417 } // context swipe
25418
25419
25420 if (capture && r.touchData.cxt) {
25421 var cxtEvt = {
25422 originalEvent: e,
25423 type: 'cxtdrag',
25424 position: {
25425 x: now[0],
25426 y: now[1]
25427 }
25428 };
25429 r.data.bgActivePosistion = undefined;
25430 r.redrawHint('select', true);
25431
25432 if (r.touchData.start) {
25433 r.touchData.start.emit(cxtEvt);
25434 } else {
25435 cy.emit(cxtEvt);
25436 }
25437
25438 if (r.touchData.start) {
25439 r.touchData.start._private.grabbed = false;
25440 }
25441
25442 r.touchData.cxtDragged = true;
25443 var near = r.findNearestElement(now[0], now[1], true, true);
25444
25445 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25446 if (r.touchData.cxtOver) {
25447 r.touchData.cxtOver.emit({
25448 originalEvent: e,
25449 type: 'cxtdragout',
25450 position: {
25451 x: now[0],
25452 y: now[1]
25453 }
25454 });
25455 }
25456
25457 r.touchData.cxtOver = near;
25458
25459 if (near) {
25460 near.emit({
25461 originalEvent: e,
25462 type: 'cxtdragover',
25463 position: {
25464 x: now[0],
25465 y: now[1]
25466 }
25467 });
25468 }
25469 } // box selection
25470
25471 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25472 e.preventDefault();
25473 r.data.bgActivePosistion = undefined;
25474 this.lastThreeTouch = +new Date();
25475
25476 if (!r.touchData.selecting) {
25477 cy.emit({
25478 originalEvent: e,
25479 type: 'boxstart',
25480 position: {
25481 x: now[0],
25482 y: now[1]
25483 }
25484 });
25485 }
25486
25487 r.touchData.selecting = true;
25488 r.touchData.didSelect = true;
25489 select[4] = 1;
25490
25491 if (!select || select.length === 0 || select[0] === undefined) {
25492 select[0] = (now[0] + now[2] + now[4]) / 3;
25493 select[1] = (now[1] + now[3] + now[5]) / 3;
25494 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25495 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25496 } else {
25497 select[2] = (now[0] + now[2] + now[4]) / 3;
25498 select[3] = (now[1] + now[3] + now[5]) / 3;
25499 }
25500
25501 r.redrawHint('select', true);
25502 r.redraw(); // pinch to zoom
25503 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25504 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25505 // two fingers => pinch to zoom
25506 e.preventDefault();
25507 r.data.bgActivePosistion = undefined;
25508 r.redrawHint('select', true);
25509 var draggedEles = r.dragData.touchDragEles;
25510
25511 if (draggedEles) {
25512 r.redrawHint('drag', true);
25513
25514 for (var i = 0; i < draggedEles.length; i++) {
25515 var de_p = draggedEles[i]._private;
25516 de_p.grabbed = false;
25517 de_p.rscratch.inDragLayer = false;
25518 }
25519 }
25520
25521 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25522
25523 var f1x2 = e.touches[0].clientX - offsetLeft,
25524 f1y2 = e.touches[0].clientY - offsetTop;
25525 var f2x2 = e.touches[1].clientX - offsetLeft,
25526 f2y2 = e.touches[1].clientY - offsetTop;
25527 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25528 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25529
25530 var factor = distance2 / distance1;
25531
25532 if (twoFingersStartInside) {
25533 // delta finger1
25534 var df1x = f1x2 - f1x1;
25535 var df1y = f1y2 - f1y1; // delta finger 2
25536
25537 var df2x = f2x2 - f2x1;
25538 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25539 // i.e. so pinching cancels out and moving together pans
25540
25541 var tx = (df1x + df2x) / 2;
25542 var ty = (df1y + df2y) / 2; // now calculate the zoom
25543
25544 var zoom1 = cy.zoom();
25545 var zoom2 = zoom1 * factor;
25546 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25547
25548 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25549 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25550 var pan2 = {
25551 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25552 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25553 }; // remove dragged eles
25554
25555 if (_start && _start.active()) {
25556 var draggedEles = r.dragData.touchDragEles;
25557 freeDraggedElements(draggedEles);
25558 r.redrawHint('drag', true);
25559 r.redrawHint('eles', true);
25560
25561 _start.unactivate().emit('freeon');
25562
25563 draggedEles.emit('free');
25564
25565 if (r.dragData.didDrag) {
25566 _start.emit('dragfreeon');
25567
25568 draggedEles.emit('dragfree');
25569 }
25570 }
25571
25572 cy.viewport({
25573 zoom: zoom2,
25574 pan: pan2,
25575 cancelOnFailedZoom: true
25576 });
25577 distance1 = distance2;
25578 f1x1 = f1x2;
25579 f1y1 = f1y2;
25580 f2x1 = f2x2;
25581 f2y1 = f2y2;
25582 r.pinching = true;
25583 } // Re-project
25584
25585
25586 if (e.touches[0]) {
25587 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25588 now[0] = pos[0];
25589 now[1] = pos[1];
25590 }
25591
25592 if (e.touches[1]) {
25593 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25594 now[2] = pos[0];
25595 now[3] = pos[1];
25596 }
25597
25598 if (e.touches[2]) {
25599 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25600 now[4] = pos[0];
25601 now[5] = pos[1];
25602 }
25603 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25604 ) {
25605 var start = r.touchData.start;
25606 var last = r.touchData.last;
25607 var near;
25608
25609 if (!r.hoverData.draggingEles && !r.swipePanning) {
25610 near = r.findNearestElement(now[0], now[1], true, true);
25611 }
25612
25613 if (capture && start != null) {
25614 e.preventDefault();
25615 } // dragging nodes
25616
25617
25618 if (capture && start != null && r.nodeIsDraggable(start)) {
25619 if (isOverThresholdDrag) {
25620 // then dragging can happen
25621 var draggedEles = r.dragData.touchDragEles;
25622 var justStartedDrag = !r.dragData.didDrag;
25623
25624 if (justStartedDrag) {
25625 addNodesToDrag(draggedEles, {
25626 inDragLayer: true
25627 });
25628 }
25629
25630 r.dragData.didDrag = true;
25631 var totalShift = {
25632 x: 0,
25633 y: 0
25634 };
25635
25636 if (number(disp[0]) && number(disp[1])) {
25637 totalShift.x += disp[0];
25638 totalShift.y += disp[1];
25639
25640 if (justStartedDrag) {
25641 r.redrawHint('eles', true);
25642 var dragDelta = r.touchData.dragDelta;
25643
25644 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25645 totalShift.x += dragDelta[0];
25646 totalShift.y += dragDelta[1];
25647 }
25648 }
25649 }
25650
25651 r.hoverData.draggingEles = true;
25652 draggedEles.silentShift(totalShift).emit('position drag');
25653 r.redrawHint('drag', true);
25654
25655 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25656 r.redrawHint('eles', true);
25657 }
25658
25659 r.redraw();
25660 } else {
25661 // otherise keep track of drag delta for later
25662 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25663
25664 if (dragDelta.length === 0) {
25665 dragDelta.push(disp[0]);
25666 dragDelta.push(disp[1]);
25667 } else {
25668 dragDelta[0] += disp[0];
25669 dragDelta[1] += disp[1];
25670 }
25671 }
25672 } // touchmove
25673
25674
25675 {
25676 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25677 x: now[0],
25678 y: now[1]
25679 });
25680
25681 if ((!start || !start.grabbed()) && near != last) {
25682 if (last) {
25683 last.emit({
25684 originalEvent: e,
25685 type: 'tapdragout',
25686 position: {
25687 x: now[0],
25688 y: now[1]
25689 }
25690 });
25691 }
25692
25693 if (near) {
25694 near.emit({
25695 originalEvent: e,
25696 type: 'tapdragover',
25697 position: {
25698 x: now[0],
25699 y: now[1]
25700 }
25701 });
25702 }
25703 }
25704
25705 r.touchData.last = near;
25706 } // check to cancel taphold
25707
25708 if (capture) {
25709 for (var i = 0; i < now.length; i++) {
25710 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25711 r.touchData.singleTouchMoved = true;
25712 }
25713 }
25714 } // panning
25715
25716
25717 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25718 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25719
25720 if (allowPassthrough) {
25721 e.preventDefault();
25722
25723 if (!r.data.bgActivePosistion) {
25724 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25725 }
25726
25727 if (r.swipePanning) {
25728 cy.panBy({
25729 x: disp[0] * zoom,
25730 y: disp[1] * zoom
25731 });
25732 } else if (isOverThresholdDrag) {
25733 r.swipePanning = true;
25734 cy.panBy({
25735 x: dx * zoom,
25736 y: dy * zoom
25737 });
25738
25739 if (start) {
25740 start.unactivate();
25741 r.redrawHint('select', true);
25742 r.touchData.start = null;
25743 }
25744 }
25745 } // Re-project
25746
25747
25748 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25749 now[0] = pos[0];
25750 now[1] = pos[1];
25751 }
25752 }
25753
25754 for (var j = 0; j < now.length; j++) {
25755 earlier[j] = now[j];
25756 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25757
25758
25759 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
25760 r.data.bgActivePosistion = undefined;
25761 r.redrawHint('select', true);
25762 r.redraw();
25763 }
25764 }, false);
25765 var touchcancelHandler;
25766 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
25767 // eslint-disable-line no-unused-vars
25768 var start = r.touchData.start;
25769 r.touchData.capture = false;
25770
25771 if (start) {
25772 start.unactivate();
25773 }
25774 });
25775 var touchendHandler;
25776 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
25777 // eslint-disable-line no-unused-vars
25778 var start = r.touchData.start;
25779 var capture = r.touchData.capture;
25780
25781 if (capture) {
25782 if (e.touches.length === 0) {
25783 r.touchData.capture = false;
25784 }
25785
25786 e.preventDefault();
25787 } else {
25788 return;
25789 }
25790
25791 var select = r.selection;
25792 r.swipePanning = false;
25793 r.hoverData.draggingEles = false;
25794 var cy = r.cy;
25795 var zoom = cy.zoom();
25796 var now = r.touchData.now;
25797 var earlier = r.touchData.earlier;
25798
25799 if (e.touches[0]) {
25800 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25801 now[0] = pos[0];
25802 now[1] = pos[1];
25803 }
25804
25805 if (e.touches[1]) {
25806 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25807 now[2] = pos[0];
25808 now[3] = pos[1];
25809 }
25810
25811 if (e.touches[2]) {
25812 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25813 now[4] = pos[0];
25814 now[5] = pos[1];
25815 }
25816
25817 if (start) {
25818 start.unactivate();
25819 }
25820
25821 var ctxTapend;
25822
25823 if (r.touchData.cxt) {
25824 ctxTapend = {
25825 originalEvent: e,
25826 type: 'cxttapend',
25827 position: {
25828 x: now[0],
25829 y: now[1]
25830 }
25831 };
25832
25833 if (start) {
25834 start.emit(ctxTapend);
25835 } else {
25836 cy.emit(ctxTapend);
25837 }
25838
25839 if (!r.touchData.cxtDragged) {
25840 var ctxTap = {
25841 originalEvent: e,
25842 type: 'cxttap',
25843 position: {
25844 x: now[0],
25845 y: now[1]
25846 }
25847 };
25848
25849 if (start) {
25850 start.emit(ctxTap);
25851 } else {
25852 cy.emit(ctxTap);
25853 }
25854 }
25855
25856 if (r.touchData.start) {
25857 r.touchData.start._private.grabbed = false;
25858 }
25859
25860 r.touchData.cxt = false;
25861 r.touchData.start = null;
25862 r.redraw();
25863 return;
25864 } // no more box selection if we don't have three fingers
25865
25866
25867 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
25868 r.touchData.selecting = false;
25869 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25870 select[0] = undefined;
25871 select[1] = undefined;
25872 select[2] = undefined;
25873 select[3] = undefined;
25874 select[4] = 0;
25875 r.redrawHint('select', true);
25876 cy.emit({
25877 type: 'boxend',
25878 originalEvent: e,
25879 position: {
25880 x: now[0],
25881 y: now[1]
25882 }
25883 });
25884
25885 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25886 return ele.selectable() && !ele.selected();
25887 };
25888
25889 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25890
25891 if (box.nonempty()) {
25892 r.redrawHint('eles', true);
25893 }
25894
25895 r.redraw();
25896 }
25897
25898 if (start != null) {
25899 start.unactivate();
25900 }
25901
25902 if (e.touches[2]) {
25903 r.data.bgActivePosistion = undefined;
25904 r.redrawHint('select', true);
25905 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
25906 r.data.bgActivePosistion = undefined;
25907 r.redrawHint('select', true);
25908 var draggedEles = r.dragData.touchDragEles;
25909
25910 if (start != null) {
25911 var startWasGrabbed = start._private.grabbed;
25912 freeDraggedElements(draggedEles);
25913 r.redrawHint('drag', true);
25914 r.redrawHint('eles', true);
25915
25916 if (startWasGrabbed) {
25917 start.emit('freeon');
25918 draggedEles.emit('free');
25919
25920 if (r.dragData.didDrag) {
25921 start.emit('dragfreeon');
25922 draggedEles.emit('dragfree');
25923 }
25924 }
25925
25926 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
25927 x: now[0],
25928 y: now[1]
25929 });
25930 start.unactivate();
25931 r.touchData.start = null;
25932 } else {
25933 var near = r.findNearestElement(now[0], now[1], true, true);
25934 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
25935 x: now[0],
25936 y: now[1]
25937 });
25938 }
25939
25940 var dx = r.touchData.startPosition[0] - now[0];
25941 var dx2 = dx * dx;
25942 var dy = r.touchData.startPosition[1] - now[1];
25943 var dy2 = dy * dy;
25944 var dist2 = dx2 + dy2;
25945 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
25946
25947 if (!r.touchData.singleTouchMoved) {
25948 if (!start) {
25949 cy.$(':selected').unselect(['tapunselect']);
25950 }
25951
25952 triggerEvents(start, ['tap', 'vclick'], e, {
25953 x: now[0],
25954 y: now[1]
25955 });
25956 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
25957
25958
25959 if (start != null && !r.dragData.didDrag // didn't drag nodes around
25960 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
25961 ) {
25962 if (cy.selectionType() === 'single') {
25963 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
25964 start.select(['tapselect']);
25965 } else {
25966 if (start.selected()) {
25967 start.unselect(['tapunselect']);
25968 } else {
25969 start.select(['tapselect']);
25970 }
25971 }
25972
25973 r.redrawHint('eles', true);
25974 }
25975
25976 r.touchData.singleTouchMoved = true;
25977 }
25978
25979 for (var j = 0; j < now.length; j++) {
25980 earlier[j] = now[j];
25981 }
25982
25983 r.dragData.didDrag = false; // reset for next touchstart
25984
25985 if (e.touches.length === 0) {
25986 r.touchData.dragDelta = [];
25987 r.touchData.startPosition = null;
25988 r.touchData.startGPosition = null;
25989 r.touchData.didSelect = false;
25990 }
25991
25992 if (e.touches.length < 2) {
25993 if (e.touches.length === 1) {
25994 // the old start global pos'n may not be the same finger that remains
25995 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
25996 }
25997
25998 r.pinching = false;
25999 r.redrawHint('eles', true);
26000 r.redraw();
26001 } //r.redraw();
26002
26003 }, false); // fallback compatibility layer for ms pointer events
26004
26005 if (typeof TouchEvent === 'undefined') {
26006 var pointers = [];
26007
26008 var makeTouch = function makeTouch(e) {
26009 return {
26010 clientX: e.clientX,
26011 clientY: e.clientY,
26012 force: 1,
26013 identifier: e.pointerId,
26014 pageX: e.pageX,
26015 pageY: e.pageY,
26016 radiusX: e.width / 2,
26017 radiusY: e.height / 2,
26018 screenX: e.screenX,
26019 screenY: e.screenY,
26020 target: e.target
26021 };
26022 };
26023
26024 var makePointer = function makePointer(e) {
26025 return {
26026 event: e,
26027 touch: makeTouch(e)
26028 };
26029 };
26030
26031 var addPointer = function addPointer(e) {
26032 pointers.push(makePointer(e));
26033 };
26034
26035 var removePointer = function removePointer(e) {
26036 for (var i = 0; i < pointers.length; i++) {
26037 var p = pointers[i];
26038
26039 if (p.event.pointerId === e.pointerId) {
26040 pointers.splice(i, 1);
26041 return;
26042 }
26043 }
26044 };
26045
26046 var updatePointer = function updatePointer(e) {
26047 var p = pointers.filter(function (p) {
26048 return p.event.pointerId === e.pointerId;
26049 })[0];
26050 p.event = e;
26051 p.touch = makeTouch(e);
26052 };
26053
26054 var addTouchesToEvent = function addTouchesToEvent(e) {
26055 e.touches = pointers.map(function (p) {
26056 return p.touch;
26057 });
26058 };
26059
26060 var pointerIsMouse = function pointerIsMouse(e) {
26061 return e.pointerType === 'mouse' || e.pointerType === 4;
26062 };
26063
26064 r.registerBinding(r.container, 'pointerdown', function (e) {
26065 if (pointerIsMouse(e)) {
26066 return;
26067 } // mouse already handled
26068
26069
26070 e.preventDefault();
26071 addPointer(e);
26072 addTouchesToEvent(e);
26073 touchstartHandler(e);
26074 });
26075 r.registerBinding(r.container, 'pointerup', function (e) {
26076 if (pointerIsMouse(e)) {
26077 return;
26078 } // mouse already handled
26079
26080
26081 removePointer(e);
26082 addTouchesToEvent(e);
26083 touchendHandler(e);
26084 });
26085 r.registerBinding(r.container, 'pointercancel', function (e) {
26086 if (pointerIsMouse(e)) {
26087 return;
26088 } // mouse already handled
26089
26090
26091 removePointer(e);
26092 addTouchesToEvent(e);
26093 touchcancelHandler(e);
26094 });
26095 r.registerBinding(r.container, 'pointermove', function (e) {
26096 if (pointerIsMouse(e)) {
26097 return;
26098 } // mouse already handled
26099
26100
26101 e.preventDefault();
26102 updatePointer(e);
26103 addTouchesToEvent(e);
26104 touchmoveHandler(e);
26105 });
26106 }
26107};
26108
26109var BRp$d = {};
26110
26111BRp$d.generatePolygon = function (name, points) {
26112 return this.nodeShapes[name] = {
26113 renderer: this,
26114 name: name,
26115 points: points,
26116 draw: function draw(context, centerX, centerY, width, height) {
26117 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26118 },
26119 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26120 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26121 },
26122 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26123 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26124 }
26125 };
26126};
26127
26128BRp$d.generateEllipse = function () {
26129 return this.nodeShapes['ellipse'] = {
26130 renderer: this,
26131 name: 'ellipse',
26132 draw: function draw(context, centerX, centerY, width, height) {
26133 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26134 },
26135 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26136 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26137 },
26138 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26139 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26140 }
26141 };
26142};
26143
26144BRp$d.generateRoundPolygon = function (name, points) {
26145 // Pre-compute control points
26146 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26147 // the unit vectors.
26148 // For simplicity the layout will be:
26149 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26150 var allPoints = new Array(points.length * 2);
26151
26152 for (var i = 0; i < points.length / 2; i++) {
26153 var sourceIndex = i * 2;
26154 var destIndex = void 0;
26155
26156 if (i < points.length / 2 - 1) {
26157 destIndex = (i + 1) * 2;
26158 } else {
26159 destIndex = 0;
26160 }
26161
26162 allPoints[i * 4] = points[sourceIndex];
26163 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26164 var xDest = points[destIndex] - points[sourceIndex];
26165 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26166 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26167 allPoints[i * 4 + 2] = xDest / norm;
26168 allPoints[i * 4 + 3] = yDest / norm;
26169 }
26170
26171 return this.nodeShapes[name] = {
26172 renderer: this,
26173 name: name,
26174 points: allPoints,
26175 draw: function draw(context, centerX, centerY, width, height) {
26176 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26177 },
26178 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26179 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26180 },
26181 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26182 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26183 }
26184 };
26185};
26186
26187BRp$d.generateRoundRectangle = function () {
26188 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26189 renderer: this,
26190 name: 'round-rectangle',
26191 points: generateUnitNgonPointsFitToSquare(4, 0),
26192 draw: function draw(context, centerX, centerY, width, height) {
26193 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26194 },
26195 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26196 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26197 },
26198 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26199 var cornerRadius = getRoundRectangleRadius(width, height);
26200 var diam = cornerRadius * 2; // Check hBox
26201
26202 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26203 return true;
26204 } // Check vBox
26205
26206
26207 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26208 return true;
26209 } // Check top left quarter circle
26210
26211
26212 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26213 return true;
26214 } // Check top right quarter circle
26215
26216
26217 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26218 return true;
26219 } // Check bottom right quarter circle
26220
26221
26222 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26223 return true;
26224 } // Check bottom left quarter circle
26225
26226
26227 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26228 return true;
26229 }
26230
26231 return false;
26232 }
26233 };
26234};
26235
26236BRp$d.generateCutRectangle = function () {
26237 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26238 renderer: this,
26239 name: 'cut-rectangle',
26240 cornerLength: getCutRectangleCornerLength(),
26241 points: generateUnitNgonPointsFitToSquare(4, 0),
26242 draw: function draw(context, centerX, centerY, width, height) {
26243 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26244 },
26245 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
26246 var cl = this.cornerLength;
26247 var hh = height / 2;
26248 var hw = width / 2;
26249 var xBegin = centerX - hw;
26250 var xEnd = centerX + hw;
26251 var yBegin = centerY - hh;
26252 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26253
26254 return {
26255 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26256 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26257 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26258 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26259 };
26260 },
26261 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26262 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26263 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26264 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26265 },
26266 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26267 // Check hBox
26268 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
26269 return true;
26270 } // Check vBox
26271
26272
26273 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
26274 return true;
26275 }
26276
26277 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26278 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26279 }
26280 };
26281};
26282
26283BRp$d.generateBarrel = function () {
26284 return this.nodeShapes['barrel'] = {
26285 renderer: this,
26286 name: 'barrel',
26287 points: generateUnitNgonPointsFitToSquare(4, 0),
26288 draw: function draw(context, centerX, centerY, width, height) {
26289 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26290 },
26291 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26292 // use two fixed t values for the bezier curve approximation
26293 var t0 = 0.15;
26294 var t1 = 0.5;
26295 var t2 = 0.85;
26296 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26297
26298 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26299 // approximate curve pts based on the two t values
26300 var m0 = qbezierPtAt({
26301 x: pts[0],
26302 y: pts[1]
26303 }, {
26304 x: pts[2],
26305 y: pts[3]
26306 }, {
26307 x: pts[4],
26308 y: pts[5]
26309 }, t0);
26310 var m1 = qbezierPtAt({
26311 x: pts[0],
26312 y: pts[1]
26313 }, {
26314 x: pts[2],
26315 y: pts[3]
26316 }, {
26317 x: pts[4],
26318 y: pts[5]
26319 }, t1);
26320 var m2 = qbezierPtAt({
26321 x: pts[0],
26322 y: pts[1]
26323 }, {
26324 x: pts[2],
26325 y: pts[3]
26326 }, {
26327 x: pts[4],
26328 y: pts[5]
26329 }, t2);
26330 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26331 };
26332
26333 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26334 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26335 },
26336 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26337 var hh = height / 2;
26338 var hw = width / 2;
26339 var xBegin = centerX - hw;
26340 var xEnd = centerX + hw;
26341 var yBegin = centerY - hh;
26342 var yEnd = centerY + hh;
26343 var curveConstants = getBarrelCurveConstants(width, height);
26344 var hOffset = curveConstants.heightOffset;
26345 var wOffset = curveConstants.widthOffset;
26346 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26347
26348 var pts = {
26349 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26350 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26351 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26352 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26353 };
26354 pts.topLeft.isTop = true;
26355 pts.topRight.isTop = true;
26356 pts.bottomLeft.isBottom = true;
26357 pts.bottomRight.isBottom = true;
26358 return pts;
26359 },
26360 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26361 var curveConstants = getBarrelCurveConstants(width, height);
26362 var hOffset = curveConstants.heightOffset;
26363 var wOffset = curveConstants.widthOffset; // Check hBox
26364
26365 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26366 return true;
26367 } // Check vBox
26368
26369
26370 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26371 return true;
26372 }
26373
26374 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26375
26376 var getCurveT = function getCurveT(x, y, curvePts) {
26377 var x0 = curvePts[4];
26378 var x1 = curvePts[2];
26379 var x2 = curvePts[0];
26380 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26381
26382 var y2 = curvePts[1];
26383 var xMin = Math.min(x0, x2);
26384 var xMax = Math.max(x0, x2);
26385 var yMin = Math.min(y0, y2);
26386 var yMax = Math.max(y0, y2);
26387
26388 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26389 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26390 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26391 var validRoots = roots.filter(function (r) {
26392 return 0 <= r && r <= 1;
26393 });
26394
26395 if (validRoots.length > 0) {
26396 return validRoots[0];
26397 }
26398 }
26399
26400 return null;
26401 };
26402
26403 var curveRegions = Object.keys(barrelCurvePts);
26404
26405 for (var i = 0; i < curveRegions.length; i++) {
26406 var corner = curveRegions[i];
26407 var cornerPts = barrelCurvePts[corner];
26408 var t = getCurveT(x, y, cornerPts);
26409
26410 if (t == null) {
26411 continue;
26412 }
26413
26414 var y0 = cornerPts[5];
26415 var y1 = cornerPts[3];
26416 var y2 = cornerPts[1];
26417 var bezY = qbezierAt(y0, y1, y2, t);
26418
26419 if (cornerPts.isTop && bezY <= y) {
26420 return true;
26421 }
26422
26423 if (cornerPts.isBottom && y <= bezY) {
26424 return true;
26425 }
26426 }
26427
26428 return false;
26429 }
26430 };
26431};
26432
26433BRp$d.generateBottomRoundrectangle = function () {
26434 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26435 renderer: this,
26436 name: 'bottom-round-rectangle',
26437 points: generateUnitNgonPointsFitToSquare(4, 0),
26438 draw: function draw(context, centerX, centerY, width, height) {
26439 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26440 },
26441 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26442 var topStartX = nodeX - (width / 2 + padding);
26443 var topStartY = nodeY - (height / 2 + padding);
26444 var topEndY = topStartY;
26445 var topEndX = nodeX + (width / 2 + padding);
26446 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26447
26448 if (topIntersections.length > 0) {
26449 return topIntersections;
26450 }
26451
26452 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26453 },
26454 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26455 var cornerRadius = getRoundRectangleRadius(width, height);
26456 var diam = 2 * cornerRadius; // Check hBox
26457
26458 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26459 return true;
26460 } // Check vBox
26461
26462
26463 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26464 return true;
26465 } // check non-rounded top side
26466
26467
26468 var outerWidth = width / 2 + 2 * padding;
26469 var outerHeight = height / 2 + 2 * padding;
26470 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26471
26472 if (pointInsidePolygonPoints(x, y, points)) {
26473 return true;
26474 } // Check bottom right quarter circle
26475
26476
26477 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26478 return true;
26479 } // Check bottom left quarter circle
26480
26481
26482 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26483 return true;
26484 }
26485
26486 return false;
26487 }
26488 };
26489};
26490
26491BRp$d.registerNodeShapes = function () {
26492 var nodeShapes = this.nodeShapes = {};
26493 var renderer = this;
26494 this.generateEllipse();
26495 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26496 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26497 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26498 nodeShapes['square'] = nodeShapes['rectangle'];
26499 this.generateRoundRectangle();
26500 this.generateCutRectangle();
26501 this.generateBarrel();
26502 this.generateBottomRoundrectangle();
26503 {
26504 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26505 this.generatePolygon('diamond', diamondPoints);
26506 this.generateRoundPolygon('round-diamond', diamondPoints);
26507 }
26508 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26509 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26510 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26511 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26512 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26513 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26514 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26515 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26516 var star5Points = new Array(20);
26517 {
26518 var outerPoints = generateUnitNgonPoints(5, 0);
26519 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26520
26521 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26522 innerRadius *= 1.57;
26523
26524 for (var i = 0; i < innerPoints.length / 2; i++) {
26525 innerPoints[i * 2] *= innerRadius;
26526 innerPoints[i * 2 + 1] *= innerRadius;
26527 }
26528
26529 for (var i = 0; i < 20 / 4; i++) {
26530 star5Points[i * 4] = outerPoints[i * 2];
26531 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26532 star5Points[i * 4 + 2] = innerPoints[i * 2];
26533 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26534 }
26535 }
26536 star5Points = fitPolygonToSquare(star5Points);
26537 this.generatePolygon('star', star5Points);
26538 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26539 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26540 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]);
26541 {
26542 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26543 this.generatePolygon('tag', tagPoints);
26544 this.generateRoundPolygon('round-tag', tagPoints);
26545 }
26546
26547 nodeShapes.makePolygon = function (points) {
26548 // use caching on user-specified polygons so they are as fast as native shapes
26549 var key = points.join('$');
26550 var name = 'polygon-' + key;
26551 var shape;
26552
26553 if (shape = this[name]) {
26554 // got cached shape
26555 return shape;
26556 } // create and cache new shape
26557
26558
26559 return renderer.generatePolygon(name, points);
26560 };
26561};
26562
26563var BRp$e = {};
26564
26565BRp$e.timeToRender = function () {
26566 return this.redrawTotalTime / this.redrawCount;
26567};
26568
26569BRp$e.redraw = function (options) {
26570 options = options || staticEmptyObject();
26571 var r = this;
26572
26573 if (r.averageRedrawTime === undefined) {
26574 r.averageRedrawTime = 0;
26575 }
26576
26577 if (r.lastRedrawTime === undefined) {
26578 r.lastRedrawTime = 0;
26579 }
26580
26581 if (r.lastDrawTime === undefined) {
26582 r.lastDrawTime = 0;
26583 }
26584
26585 r.requestedFrame = true;
26586 r.renderOptions = options;
26587};
26588
26589BRp$e.beforeRender = function (fn, priority) {
26590 // the renderer can't add tick callbacks when destroyed
26591 if (this.destroyed) {
26592 return;
26593 }
26594
26595 if (priority == null) {
26596 error('Priority is not optional for beforeRender');
26597 }
26598
26599 var cbs = this.beforeRenderCallbacks;
26600 cbs.push({
26601 fn: fn,
26602 priority: priority
26603 }); // higher priority callbacks executed first
26604
26605 cbs.sort(function (a, b) {
26606 return b.priority - a.priority;
26607 });
26608};
26609
26610var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26611 var cbs = r.beforeRenderCallbacks;
26612
26613 for (var i = 0; i < cbs.length; i++) {
26614 cbs[i].fn(willDraw, startTime);
26615 }
26616};
26617
26618BRp$e.startRenderLoop = function () {
26619 var r = this;
26620 var cy = r.cy;
26621
26622 if (r.renderLoopStarted) {
26623 return;
26624 } else {
26625 r.renderLoopStarted = true;
26626 }
26627
26628 var renderFn = function renderFn(requestTime) {
26629 if (r.destroyed) {
26630 return;
26631 }
26632
26633 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26634 beforeRenderCallbacks(r, true, requestTime);
26635 var startTime = performanceNow();
26636 r.render(r.renderOptions);
26637 var endTime = r.lastDrawTime = performanceNow();
26638
26639 if (r.averageRedrawTime === undefined) {
26640 r.averageRedrawTime = endTime - startTime;
26641 }
26642
26643 if (r.redrawCount === undefined) {
26644 r.redrawCount = 0;
26645 }
26646
26647 r.redrawCount++;
26648
26649 if (r.redrawTotalTime === undefined) {
26650 r.redrawTotalTime = 0;
26651 }
26652
26653 var duration = endTime - startTime;
26654 r.redrawTotalTime += duration;
26655 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26656
26657 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26658 r.requestedFrame = false;
26659 } else {
26660 beforeRenderCallbacks(r, false, requestTime);
26661 }
26662
26663 r.skipFrame = false;
26664 requestAnimationFrame(renderFn);
26665 };
26666
26667 requestAnimationFrame(renderFn);
26668};
26669
26670var BaseRenderer = function BaseRenderer(options) {
26671 this.init(options);
26672};
26673
26674var BR = BaseRenderer;
26675var BRp$f = BR.prototype;
26676BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26677
26678BRp$f.init = function (options) {
26679 var r = this;
26680 r.options = options;
26681 r.cy = options.cy;
26682 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26683
26684 if (window$1) {
26685 var document = window$1.document;
26686 var head = document.head;
26687 var stylesheetId = '__________cytoscape_stylesheet';
26688 var className = '__________cytoscape_container';
26689 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26690
26691 if (ctr.className.indexOf(className) < 0) {
26692 ctr.className = (ctr.className || '') + ' ' + className;
26693 }
26694
26695 if (!stylesheetAlreadyExists) {
26696 var stylesheet = document.createElement('style');
26697 stylesheet.id = stylesheetId;
26698 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26699 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26700 }
26701
26702 var computedStyle = window$1.getComputedStyle(ctr);
26703 var position = computedStyle.getPropertyValue('position');
26704
26705 if (position === 'static') {
26706 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26707 }
26708 }
26709
26710 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26711
26712 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
26713
26714 r.hoverData = {
26715 down: null,
26716 last: null,
26717 downTime: null,
26718 triggerMode: null,
26719 dragging: false,
26720 initialPan: [null, null],
26721 capture: false
26722 };
26723 r.dragData = {
26724 possibleDragElements: []
26725 };
26726 r.touchData = {
26727 start: null,
26728 capture: false,
26729 // These 3 fields related to tap, taphold events
26730 startPosition: [null, null, null, null, null, null],
26731 singleTouchStartTime: null,
26732 singleTouchMoved: true,
26733 now: [null, null, null, null, null, null],
26734 earlier: [null, null, null, null, null, null]
26735 };
26736 r.redraws = 0;
26737 r.showFps = options.showFps;
26738 r.debug = options.debug;
26739 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
26740 r.textureOnViewport = options.textureOnViewport;
26741 r.wheelSensitivity = options.wheelSensitivity;
26742 r.motionBlurEnabled = options.motionBlur; // on by default
26743
26744 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
26745 r.motionBlur = options.motionBlur; // for initial kick off
26746
26747 r.motionBlurOpacity = options.motionBlurOpacity;
26748 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
26749 r.motionBlurPxRatio = 1;
26750 r.mbPxRBlurry = 1; //0.8;
26751
26752 r.minMbLowQualFrames = 4;
26753 r.fullQualityMb = false;
26754 r.clearedForMotionBlur = [];
26755 r.desktopTapThreshold = options.desktopTapThreshold;
26756 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
26757 r.touchTapThreshold = options.touchTapThreshold;
26758 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
26759 r.tapholdDuration = 500;
26760 r.bindings = [];
26761 r.beforeRenderCallbacks = [];
26762 r.beforeRenderPriorities = {
26763 // higher priority execs before lower one
26764 animations: 400,
26765 eleCalcs: 300,
26766 eleTxrDeq: 200,
26767 lyrTxrDeq: 150,
26768 lyrTxrSkip: 100
26769 };
26770 r.registerNodeShapes();
26771 r.registerArrowShapes();
26772 r.registerCalculationListeners();
26773};
26774
26775BRp$f.notify = function (eventName, eles) {
26776 var r = this;
26777 var cy = r.cy; // the renderer can't be notified after it's destroyed
26778
26779 if (this.destroyed) {
26780 return;
26781 }
26782
26783 if (eventName === 'init') {
26784 r.load();
26785 return;
26786 }
26787
26788 if (eventName === 'destroy') {
26789 r.destroy();
26790 return;
26791 }
26792
26793 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
26794 r.invalidateCachedZSortedEles();
26795 }
26796
26797 if (eventName === 'viewport') {
26798 r.redrawHint('select', true);
26799 }
26800
26801 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
26802 r.invalidateContainerClientCoordsCache();
26803 r.matchCanvasSize(r.container);
26804 }
26805
26806 r.redrawHint('eles', true);
26807 r.redrawHint('drag', true);
26808 this.startRenderLoop();
26809 this.redraw();
26810};
26811
26812BRp$f.destroy = function () {
26813 var r = this;
26814 r.destroyed = true;
26815 r.cy.stopAnimationLoop();
26816
26817 for (var i = 0; i < r.bindings.length; i++) {
26818 var binding = r.bindings[i];
26819 var b = binding;
26820 var tgt = b.target;
26821 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
26822 }
26823
26824 r.bindings = [];
26825 r.beforeRenderCallbacks = [];
26826 r.onUpdateEleCalcsFns = [];
26827
26828 if (r.removeObserver) {
26829 r.removeObserver.disconnect();
26830 }
26831
26832 if (r.styleObserver) {
26833 r.styleObserver.disconnect();
26834 }
26835
26836 if (r.resizeObserver) {
26837 r.resizeObserver.disconnect();
26838 }
26839
26840 if (r.labelCalcDiv) {
26841 try {
26842 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
26843 } catch (e) {// ie10 issue #1014
26844 }
26845 }
26846};
26847
26848BRp$f.isHeadless = function () {
26849 return false;
26850};
26851
26852[BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
26853 extend(BRp$f, props);
26854});
26855
26856var fullFpsTime = 1000 / 60; // assume 60 frames per second
26857
26858var defs = {
26859 setupDequeueing: function setupDequeueing(opts) {
26860 return function setupDequeueingImpl() {
26861 var self = this;
26862 var r = this.renderer;
26863
26864 if (self.dequeueingSetup) {
26865 return;
26866 } else {
26867 self.dequeueingSetup = true;
26868 }
26869
26870 var queueRedraw = util(function () {
26871 r.redrawHint('eles', true);
26872 r.redrawHint('drag', true);
26873 r.redraw();
26874 }, opts.deqRedrawThreshold);
26875
26876 var dequeue = function dequeue(willDraw, frameStartTime) {
26877 var startTime = performanceNow();
26878 var avgRenderTime = r.averageRedrawTime;
26879 var renderTime = r.lastRedrawTime;
26880 var deqd = [];
26881 var extent = r.cy.extent();
26882 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
26883 // queue won't automatically be flushed before dequeueing starts
26884
26885 if (!willDraw) {
26886 r.flushRenderedStyleQueue();
26887 }
26888
26889 while (true) {
26890 // eslint-disable-line no-constant-condition
26891 var now = performanceNow();
26892 var duration = now - startTime;
26893 var frameDuration = now - frameStartTime;
26894
26895 if (renderTime < fullFpsTime) {
26896 // if we're rendering faster than the ideal fps, then do dequeueing
26897 // during all of the remaining frame time
26898 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
26899
26900 if (frameDuration >= opts.deqFastCost * timeAvailable) {
26901 break;
26902 }
26903 } else {
26904 if (willDraw) {
26905 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
26906 break;
26907 }
26908 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
26909 break;
26910 }
26911 }
26912
26913 var thisDeqd = opts.deq(self, pixelRatio, extent);
26914
26915 if (thisDeqd.length > 0) {
26916 for (var i = 0; i < thisDeqd.length; i++) {
26917 deqd.push(thisDeqd[i]);
26918 }
26919 } else {
26920 break;
26921 }
26922 } // callbacks on dequeue
26923
26924
26925 if (deqd.length > 0) {
26926 opts.onDeqd(self, deqd);
26927
26928 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
26929 queueRedraw();
26930 }
26931 }
26932 };
26933
26934 var priority = opts.priority || noop;
26935 r.beforeRender(dequeue, priority(self));
26936 };
26937 }
26938};
26939
26940// Uses keys so elements may share the same cache.
26941
26942var ElementTextureCacheLookup =
26943/*#__PURE__*/
26944function () {
26945 function ElementTextureCacheLookup(getKey) {
26946 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
26947
26948 _classCallCheck(this, ElementTextureCacheLookup);
26949
26950 this.idsByKey = new Map$1();
26951 this.keyForId = new Map$1();
26952 this.cachesByLvl = new Map$1();
26953 this.lvls = [];
26954 this.getKey = getKey;
26955 this.doesEleInvalidateKey = doesEleInvalidateKey;
26956 }
26957
26958 _createClass(ElementTextureCacheLookup, [{
26959 key: "getIdsFor",
26960 value: function getIdsFor(key) {
26961 if (key == null) {
26962 error("Can not get id list for null key");
26963 }
26964
26965 var idsByKey = this.idsByKey;
26966 var ids = this.idsByKey.get(key);
26967
26968 if (!ids) {
26969 ids = new Set$1();
26970 idsByKey.set(key, ids);
26971 }
26972
26973 return ids;
26974 }
26975 }, {
26976 key: "addIdForKey",
26977 value: function addIdForKey(key, id) {
26978 if (key != null) {
26979 this.getIdsFor(key).add(id);
26980 }
26981 }
26982 }, {
26983 key: "deleteIdForKey",
26984 value: function deleteIdForKey(key, id) {
26985 if (key != null) {
26986 this.getIdsFor(key)["delete"](id);
26987 }
26988 }
26989 }, {
26990 key: "getNumberOfIdsForKey",
26991 value: function getNumberOfIdsForKey(key) {
26992 if (key == null) {
26993 return 0;
26994 } else {
26995 return this.getIdsFor(key).size;
26996 }
26997 }
26998 }, {
26999 key: "updateKeyMappingFor",
27000 value: function updateKeyMappingFor(ele) {
27001 var id = ele.id();
27002 var prevKey = this.keyForId.get(id);
27003 var currKey = this.getKey(ele);
27004 this.deleteIdForKey(prevKey, id);
27005 this.addIdForKey(currKey, id);
27006 this.keyForId.set(id, currKey);
27007 }
27008 }, {
27009 key: "deleteKeyMappingFor",
27010 value: function deleteKeyMappingFor(ele) {
27011 var id = ele.id();
27012 var prevKey = this.keyForId.get(id);
27013 this.deleteIdForKey(prevKey, id);
27014 this.keyForId["delete"](id);
27015 }
27016 }, {
27017 key: "keyHasChangedFor",
27018 value: function keyHasChangedFor(ele) {
27019 var id = ele.id();
27020 var prevKey = this.keyForId.get(id);
27021 var newKey = this.getKey(ele);
27022 return prevKey !== newKey;
27023 }
27024 }, {
27025 key: "isInvalid",
27026 value: function isInvalid(ele) {
27027 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27028 }
27029 }, {
27030 key: "getCachesAt",
27031 value: function getCachesAt(lvl) {
27032 var cachesByLvl = this.cachesByLvl,
27033 lvls = this.lvls;
27034 var caches = cachesByLvl.get(lvl);
27035
27036 if (!caches) {
27037 caches = new Map$1();
27038 cachesByLvl.set(lvl, caches);
27039 lvls.push(lvl);
27040 }
27041
27042 return caches;
27043 }
27044 }, {
27045 key: "getCache",
27046 value: function getCache(key, lvl) {
27047 return this.getCachesAt(lvl).get(key);
27048 }
27049 }, {
27050 key: "get",
27051 value: function get(ele, lvl) {
27052 var key = this.getKey(ele);
27053 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27054
27055 if (cache != null) {
27056 this.updateKeyMappingFor(ele);
27057 }
27058
27059 return cache;
27060 }
27061 }, {
27062 key: "getForCachedKey",
27063 value: function getForCachedKey(ele, lvl) {
27064 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27065
27066 var cache = this.getCache(key, lvl);
27067 return cache;
27068 }
27069 }, {
27070 key: "hasCache",
27071 value: function hasCache(key, lvl) {
27072 return this.getCachesAt(lvl).has(key);
27073 }
27074 }, {
27075 key: "has",
27076 value: function has(ele, lvl) {
27077 var key = this.getKey(ele);
27078 return this.hasCache(key, lvl);
27079 }
27080 }, {
27081 key: "setCache",
27082 value: function setCache(key, lvl, cache) {
27083 cache.key = key;
27084 this.getCachesAt(lvl).set(key, cache);
27085 }
27086 }, {
27087 key: "set",
27088 value: function set(ele, lvl, cache) {
27089 var key = this.getKey(ele);
27090 this.setCache(key, lvl, cache);
27091 this.updateKeyMappingFor(ele);
27092 }
27093 }, {
27094 key: "deleteCache",
27095 value: function deleteCache(key, lvl) {
27096 this.getCachesAt(lvl)["delete"](key);
27097 }
27098 }, {
27099 key: "delete",
27100 value: function _delete(ele, lvl) {
27101 var key = this.getKey(ele);
27102 this.deleteCache(key, lvl);
27103 }
27104 }, {
27105 key: "invalidateKey",
27106 value: function invalidateKey(key) {
27107 var _this = this;
27108
27109 this.lvls.forEach(function (lvl) {
27110 return _this.deleteCache(key, lvl);
27111 });
27112 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27113
27114 }, {
27115 key: "invalidate",
27116 value: function invalidate(ele) {
27117 var id = ele.id();
27118 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27119
27120 this.deleteKeyMappingFor(ele);
27121 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27122
27123 if (entireKeyInvalidated) {
27124 // clear mapping for current key
27125 this.invalidateKey(key);
27126 }
27127
27128 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27129 }
27130 }]);
27131
27132 return ElementTextureCacheLookup;
27133}();
27134
27135var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27136
27137var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27138
27139var minLvl = -4; // when scaling smaller than that we don't need to re-render
27140
27141var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27142
27143var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27144
27145var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27146
27147var defTxrWidth = 1024; // default/minimum texture width
27148
27149var maxTxrW = 1024; // the maximum width of a texture
27150
27151var maxTxrH = 1024; // the maximum height of a texture
27152
27153var minUtility = 0.2; // if usage of texture is less than this, it is retired
27154
27155var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27156
27157var maxFullnessChecks = 10; // dequeued after this many checks
27158
27159var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27160
27161var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27162
27163var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27164
27165var deqFastCost = 0.9; // % of frame time to be used when >60fps
27166
27167var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27168
27169var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27170
27171var getTxrReasons = {
27172 dequeue: 'dequeue',
27173 downscale: 'downscale',
27174 highQuality: 'highQuality'
27175};
27176var initDefaults = defaults({
27177 getKey: null,
27178 doesEleInvalidateKey: falsify,
27179 drawElement: null,
27180 getBoundingBox: null,
27181 getRotationPoint: null,
27182 getRotationOffset: null,
27183 isVisible: trueify,
27184 allowEdgeTxrCaching: true,
27185 allowParentTxrCaching: true
27186});
27187
27188var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27189 var self = this;
27190 self.renderer = renderer;
27191 self.onDequeues = [];
27192 var opts = initDefaults(initOptions);
27193 extend(self, opts);
27194 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27195 self.setupDequeueing();
27196};
27197
27198var ETCp = ElementTextureCache.prototype;
27199ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27200
27201ETCp.getTextureQueue = function (txrH) {
27202 var self = this;
27203 self.eleImgCaches = self.eleImgCaches || {};
27204 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27205}; // the list of usused textures which can be recycled (in use in texture queue)
27206
27207
27208ETCp.getRetiredTextureQueue = function (txrH) {
27209 var self = this;
27210 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27211 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27212 return rtxtrQ;
27213}; // queue of element draw requests at different scale levels
27214
27215
27216ETCp.getElementQueue = function () {
27217 var self = this;
27218 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
27219 return b.reqs - a.reqs;
27220 });
27221 return q;
27222}; // queue of element draw requests at different scale levels (element id lookup)
27223
27224
27225ETCp.getElementKeyToQueue = function () {
27226 var self = this;
27227 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27228 return k2q;
27229};
27230
27231ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27232 var self = this;
27233 var r = this.renderer;
27234 var zoom = r.cy.zoom();
27235 var lookup = this.lookup;
27236
27237 if (bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible()) {
27238 return null;
27239 }
27240
27241 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27242 return null;
27243 }
27244
27245 if (lvl == null) {
27246 lvl = Math.ceil(log2(zoom * pxRatio));
27247 }
27248
27249 if (lvl < minLvl) {
27250 lvl = minLvl;
27251 } else if (zoom >= maxZoom || lvl > maxLvl) {
27252 return null;
27253 }
27254
27255 var scale = Math.pow(2, lvl);
27256 var eleScaledH = bb.h * scale;
27257 var eleScaledW = bb.w * scale;
27258 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27259
27260 if (!this.isVisible(ele, scaledLabelShown)) {
27261 return null;
27262 }
27263
27264 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
27265
27266 if (eleCache && eleCache.invalidated) {
27267 eleCache.invalidated = false;
27268 eleCache.texture.invalidatedWidth -= eleCache.width;
27269 }
27270
27271 if (eleCache) {
27272 return eleCache;
27273 }
27274
27275 var txrH; // which texture height this ele belongs to
27276
27277 if (eleScaledH <= minTxrH) {
27278 txrH = minTxrH;
27279 } else if (eleScaledH <= txrStepH) {
27280 txrH = txrStepH;
27281 } else {
27282 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27283 }
27284
27285 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27286 return null; // caching large elements is not efficient
27287 }
27288
27289 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
27290
27291 var txr = txrQ[txrQ.length - 2];
27292
27293 var addNewTxr = function addNewTxr() {
27294 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27295 }; // try the last one if there is no second last one
27296
27297
27298 if (!txr) {
27299 txr = txrQ[txrQ.length - 1];
27300 } // if the last one doesn't exist, we need a first one
27301
27302
27303 if (!txr) {
27304 txr = addNewTxr();
27305 } // if there's no room in the current texture, we need a new one
27306
27307
27308 if (txr.width - txr.usedWidth < eleScaledW) {
27309 txr = addNewTxr();
27310 }
27311
27312 var scalableFrom = function scalableFrom(otherCache) {
27313 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27314 };
27315
27316 var deqing = reason && reason === getTxrReasons.dequeue;
27317 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27318 var downscaleReq = reason && reason === getTxrReasons.downscale;
27319 var higherCache; // the nearest cache with a higher level
27320
27321 for (var l = lvl + 1; l <= maxLvl; l++) {
27322 var c = lookup.get(ele, l);
27323
27324 if (c) {
27325 higherCache = c;
27326 break;
27327 }
27328 }
27329
27330 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27331
27332 var downscale = function downscale() {
27333 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27334 }; // reset ele area in texture
27335
27336
27337 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27338 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27339
27340 if (scalableFrom(oneUpCache)) {
27341 // then we can relatively cheaply rescale the existing image w/o rerendering
27342 downscale();
27343 } else if (scalableFrom(higherCache)) {
27344 // then use the higher cache for now and queue the next level down
27345 // to cheaply scale towards the smaller level
27346 if (highQualityReq) {
27347 for (var _l = higherCache.level; _l > lvl; _l--) {
27348 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27349 }
27350
27351 downscale();
27352 } else {
27353 self.queueElement(ele, higherCache.level - 1);
27354 return higherCache;
27355 }
27356 } else {
27357 var lowerCache; // the nearest cache with a lower level
27358
27359 if (!deqing && !highQualityReq && !downscaleReq) {
27360 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
27361 var _c = lookup.get(ele, _l2);
27362
27363 if (_c) {
27364 lowerCache = _c;
27365 break;
27366 }
27367 }
27368 }
27369
27370 if (scalableFrom(lowerCache)) {
27371 // then use the lower quality cache for now and queue the better one for later
27372 self.queueElement(ele, lvl);
27373 return lowerCache;
27374 }
27375
27376 txr.context.translate(txr.usedWidth, 0);
27377 txr.context.scale(scale, scale);
27378 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27379 txr.context.scale(1 / scale, 1 / scale);
27380 txr.context.translate(-txr.usedWidth, 0);
27381 }
27382
27383 eleCache = {
27384 x: txr.usedWidth,
27385 texture: txr,
27386 level: lvl,
27387 scale: scale,
27388 width: eleScaledW,
27389 height: eleScaledH,
27390 scaledLabelShown: scaledLabelShown
27391 };
27392 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27393 txr.eleCaches.push(eleCache);
27394 lookup.set(ele, lvl, eleCache);
27395 self.checkTextureFullness(txr);
27396 return eleCache;
27397};
27398
27399ETCp.invalidateElements = function (eles) {
27400 for (var i = 0; i < eles.length; i++) {
27401 this.invalidateElement(eles[i]);
27402 }
27403};
27404
27405ETCp.invalidateElement = function (ele) {
27406 var self = this;
27407 var lookup = self.lookup;
27408 var caches = [];
27409 var invalid = lookup.isInvalid(ele);
27410
27411 if (!invalid) {
27412 return; // override the invalidation request if the element key has not changed
27413 }
27414
27415 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
27416 var cache = lookup.getForCachedKey(ele, lvl);
27417
27418 if (cache) {
27419 caches.push(cache);
27420 }
27421 }
27422
27423 var noOtherElesUseCache = lookup.invalidate(ele);
27424
27425 if (noOtherElesUseCache) {
27426 for (var i = 0; i < caches.length; i++) {
27427 var _cache = caches[i];
27428 var txr = _cache.texture; // remove space from the texture it belongs to
27429
27430 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27431
27432 _cache.invalidated = true; // retire the texture if its utility is low
27433
27434 self.checkTextureUtility(txr);
27435 }
27436 } // remove from queue since the old req was for the old state
27437
27438
27439 self.removeFromQueue(ele);
27440};
27441
27442ETCp.checkTextureUtility = function (txr) {
27443 // invalidate all entries in the cache if the cache size is small
27444 if (txr.invalidatedWidth >= minUtility * txr.width) {
27445 this.retireTexture(txr);
27446 }
27447};
27448
27449ETCp.checkTextureFullness = function (txr) {
27450 // if texture has been mostly filled and passed over several times, remove
27451 // it from the queue so we don't need to waste time looking at it to put new things
27452 var self = this;
27453 var txrQ = self.getTextureQueue(txr.height);
27454
27455 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27456 removeFromArray(txrQ, txr);
27457 } else {
27458 txr.fullnessChecks++;
27459 }
27460};
27461
27462ETCp.retireTexture = function (txr) {
27463 var self = this;
27464 var txrH = txr.height;
27465 var txrQ = self.getTextureQueue(txrH);
27466 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27467
27468 removeFromArray(txrQ, txr);
27469 txr.retired = true; // remove the refs from the eles to the caches:
27470
27471 var eleCaches = txr.eleCaches;
27472
27473 for (var i = 0; i < eleCaches.length; i++) {
27474 var eleCache = eleCaches[i];
27475 lookup.deleteCache(eleCache.key, eleCache.level);
27476 }
27477
27478 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27479
27480 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27481 rtxtrQ.push(txr);
27482};
27483
27484ETCp.addTexture = function (txrH, minW) {
27485 var self = this;
27486 var txrQ = self.getTextureQueue(txrH);
27487 var txr = {};
27488 txrQ.push(txr);
27489 txr.eleCaches = [];
27490 txr.height = txrH;
27491 txr.width = Math.max(defTxrWidth, minW);
27492 txr.usedWidth = 0;
27493 txr.invalidatedWidth = 0;
27494 txr.fullnessChecks = 0;
27495 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27496 txr.context = txr.canvas.getContext('2d');
27497 return txr;
27498};
27499
27500ETCp.recycleTexture = function (txrH, minW) {
27501 var self = this;
27502 var txrQ = self.getTextureQueue(txrH);
27503 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27504
27505 for (var i = 0; i < rtxtrQ.length; i++) {
27506 var txr = rtxtrQ[i];
27507
27508 if (txr.width >= minW) {
27509 txr.retired = false;
27510 txr.usedWidth = 0;
27511 txr.invalidatedWidth = 0;
27512 txr.fullnessChecks = 0;
27513 clearArray(txr.eleCaches);
27514 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27515 txr.context.clearRect(0, 0, txr.width, txr.height);
27516 removeFromArray(rtxtrQ, txr);
27517 txrQ.push(txr);
27518 return txr;
27519 }
27520 }
27521};
27522
27523ETCp.queueElement = function (ele, lvl) {
27524 var self = this;
27525 var q = self.getElementQueue();
27526 var k2q = self.getElementKeyToQueue();
27527 var key = this.getKey(ele);
27528 var existingReq = k2q[key];
27529
27530 if (existingReq) {
27531 // use the max lvl b/c in between lvls are cheap to make
27532 existingReq.level = Math.max(existingReq.level, lvl);
27533 existingReq.eles.merge(ele);
27534 existingReq.reqs++;
27535 q.updateItem(existingReq);
27536 } else {
27537 var req = {
27538 eles: ele.spawn().merge(ele),
27539 level: lvl,
27540 reqs: 1,
27541 key: key
27542 };
27543 q.push(req);
27544 k2q[key] = req;
27545 }
27546};
27547
27548ETCp.dequeue = function (pxRatio
27549/*, extent*/
27550) {
27551 var self = this;
27552 var q = self.getElementQueue();
27553 var k2q = self.getElementKeyToQueue();
27554 var dequeued = [];
27555 var lookup = self.lookup;
27556
27557 for (var i = 0; i < maxDeqSize; i++) {
27558 if (q.size() > 0) {
27559 var req = q.pop();
27560 var key = req.key;
27561 var ele = req.eles[0]; // all eles have the same key
27562
27563 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27564
27565 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27566
27567 if (cacheExists) {
27568 continue;
27569 }
27570
27571 dequeued.push(req);
27572 var bb = self.getBoundingBox(ele);
27573 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27574 } else {
27575 break;
27576 }
27577 }
27578
27579 return dequeued;
27580};
27581
27582ETCp.removeFromQueue = function (ele) {
27583 var self = this;
27584 var q = self.getElementQueue();
27585 var k2q = self.getElementKeyToQueue();
27586 var key = this.getKey(ele);
27587 var req = k2q[key];
27588
27589 if (req != null) {
27590 if (req.eles.length === 1) {
27591 // remove if last ele in the req
27592 // bring to front of queue
27593 req.reqs = MAX_INT;
27594 q.updateItem(req);
27595 q.pop(); // remove from queue
27596
27597 k2q[key] = null; // remove from lookup map
27598 } else {
27599 // otherwise just remove ele from req
27600 req.eles.unmerge(ele);
27601 }
27602 }
27603};
27604
27605ETCp.onDequeue = function (fn) {
27606 this.onDequeues.push(fn);
27607};
27608
27609ETCp.offDequeue = function (fn) {
27610 removeFromArray(this.onDequeues, fn);
27611};
27612
27613ETCp.setupDequeueing = defs.setupDequeueing({
27614 deqRedrawThreshold: deqRedrawThreshold,
27615 deqCost: deqCost,
27616 deqAvgCost: deqAvgCost,
27617 deqNoDrawCost: deqNoDrawCost,
27618 deqFastCost: deqFastCost,
27619 deq: function deq(self, pxRatio, extent) {
27620 return self.dequeue(pxRatio, extent);
27621 },
27622 onDeqd: function onDeqd(self, deqd) {
27623 for (var i = 0; i < self.onDequeues.length; i++) {
27624 var fn = self.onDequeues[i];
27625 fn(deqd);
27626 }
27627 },
27628 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27629 for (var i = 0; i < deqd.length; i++) {
27630 var eles = deqd[i].eles;
27631
27632 for (var j = 0; j < eles.length; j++) {
27633 var bb = eles[j].boundingBox();
27634
27635 if (boundingBoxesIntersect(bb, extent)) {
27636 return true;
27637 }
27638 }
27639 }
27640
27641 return false;
27642 },
27643 priority: function priority(self) {
27644 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27645 }
27646});
27647
27648var defNumLayers = 1; // default number of layers to use
27649
27650var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27651
27652var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
27653
27654var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
27655
27656var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27657
27658var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27659
27660var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27661
27662var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27663
27664var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27665
27666var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27667
27668var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27669
27670var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27671
27672var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27673
27674var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27675// var log = function(){ console.log.apply( console, arguments ); };
27676
27677var LayeredTextureCache = function LayeredTextureCache(renderer) {
27678 var self = this;
27679 var r = self.renderer = renderer;
27680 var cy = r.cy;
27681 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27682
27683 self.firstGet = true;
27684 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27685 self.skipping = false;
27686 self.eleTxrDeqs = cy.collection();
27687 self.scheduleElementRefinement = util(function () {
27688 self.refineElementTextures(self.eleTxrDeqs);
27689 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27690 }, refineEleDebounceTime);
27691 r.beforeRender(function (willDraw, now) {
27692 if (now - self.lastInvalidationTime <= invalidThreshold) {
27693 self.skipping = true;
27694 } else {
27695 self.skipping = false;
27696 }
27697 }, r.beforeRenderPriorities.lyrTxrSkip);
27698
27699 var qSort = function qSort(a, b) {
27700 return b.reqs - a.reqs;
27701 };
27702
27703 self.layersQueue = new Heap(qSort);
27704 self.setupDequeueing();
27705};
27706
27707var LTCp = LayeredTextureCache.prototype;
27708var layerIdPool = 0;
27709var MAX_INT$1 = Math.pow(2, 53) - 1;
27710
27711LTCp.makeLayer = function (bb, lvl) {
27712 var scale = Math.pow(2, lvl);
27713 var w = Math.ceil(bb.w * scale);
27714 var h = Math.ceil(bb.h * scale);
27715 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27716 var layer = {
27717 id: layerIdPool = ++layerIdPool % MAX_INT$1,
27718 bb: bb,
27719 level: lvl,
27720 width: w,
27721 height: h,
27722 canvas: canvas,
27723 context: canvas.getContext('2d'),
27724 eles: [],
27725 elesQueue: [],
27726 reqs: 0
27727 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27728
27729 var cxt = layer.context;
27730 var dx = -layer.bb.x1;
27731 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
27732
27733 cxt.scale(scale, scale);
27734 cxt.translate(dx, dy);
27735 return layer;
27736};
27737
27738LTCp.getLayers = function (eles, pxRatio, lvl) {
27739 var self = this;
27740 var r = self.renderer;
27741 var cy = r.cy;
27742 var zoom = cy.zoom();
27743 var firstGet = self.firstGet;
27744 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
27745 //log eles.map(function(ele){ return ele.id() }) );
27746
27747 if (lvl == null) {
27748 lvl = Math.ceil(log2(zoom * pxRatio));
27749
27750 if (lvl < minLvl$1) {
27751 lvl = minLvl$1;
27752 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27753 return null;
27754 }
27755 }
27756
27757 self.validateLayersElesOrdering(lvl, eles);
27758 var layersByLvl = self.layersByLevel;
27759 var scale = Math.pow(2, lvl);
27760 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
27761 var bb;
27762 var lvlComplete = self.levelIsComplete(lvl, eles);
27763 var tmpLayers;
27764
27765 var checkTempLevels = function checkTempLevels() {
27766 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
27767 self.validateLayersElesOrdering(l, eles);
27768
27769 if (self.levelIsComplete(l, eles)) {
27770 tmpLayers = layersByLvl[l];
27771 return true;
27772 }
27773 };
27774
27775 var checkLvls = function checkLvls(dir) {
27776 if (tmpLayers) {
27777 return;
27778 }
27779
27780 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
27781 if (canUseAsTmpLvl(l)) {
27782 break;
27783 }
27784 }
27785 };
27786
27787 checkLvls(+1);
27788 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
27789
27790 for (var i = layers.length - 1; i >= 0; i--) {
27791 var layer = layers[i];
27792
27793 if (layer.invalid) {
27794 removeFromArray(layers, layer);
27795 }
27796 }
27797 };
27798
27799 if (!lvlComplete) {
27800 // if the current level is incomplete, then use the closest, best quality layerset temporarily
27801 // and later queue the current layerset so we can get the proper quality level soon
27802 checkTempLevels();
27803 } else {
27804 // log('level complete, using existing layers\n--');
27805 return layers;
27806 }
27807
27808 var getBb = function getBb() {
27809 if (!bb) {
27810 bb = makeBoundingBox();
27811
27812 for (var i = 0; i < eles.length; i++) {
27813 updateBoundingBox(bb, eles[i].boundingBox());
27814 }
27815 }
27816
27817 return bb;
27818 };
27819
27820 var makeLayer = function makeLayer(opts) {
27821 opts = opts || {};
27822 var after = opts.after;
27823 getBb();
27824 var area = bb.w * scale * (bb.h * scale);
27825
27826 if (area > maxLayerArea) {
27827 return null;
27828 }
27829
27830 var layer = self.makeLayer(bb, lvl);
27831
27832 if (after != null) {
27833 var index = layers.indexOf(after) + 1;
27834 layers.splice(index, 0, layer);
27835 } else if (opts.insert === undefined || opts.insert) {
27836 // no after specified => first layer made so put at start
27837 layers.unshift(layer);
27838 } // if( tmpLayers ){
27839 //self.queueLayer( layer );
27840 // }
27841
27842
27843 return layer;
27844 };
27845
27846 if (self.skipping && !firstGet) {
27847 // log('skip layers');
27848 return null;
27849 } // log('do layers');
27850
27851
27852 var layer = null;
27853 var maxElesPerLayer = eles.length / defNumLayers;
27854 var allowLazyQueueing = !firstGet;
27855
27856 for (var i = 0; i < eles.length; i++) {
27857 var ele = eles[i];
27858 var rs = ele._private.rscratch;
27859 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
27860
27861 var existingLayer = caches[lvl];
27862
27863 if (existingLayer) {
27864 // reuse layer for later eles
27865 // log('reuse layer for', ele.id());
27866 layer = existingLayer;
27867 continue;
27868 }
27869
27870 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
27871 // log('make new layer for ele %s', ele.id());
27872 layer = makeLayer({
27873 insert: true,
27874 after: layer
27875 }); // if now layer can be built then we can't use layers at this level
27876
27877 if (!layer) {
27878 return null;
27879 } // log('new layer with id %s', layer.id);
27880
27881 }
27882
27883 if (tmpLayers || allowLazyQueueing) {
27884 // log('queue ele %s in layer %s', ele.id(), layer.id);
27885 self.queueLayer(layer, ele);
27886 } else {
27887 // log('draw ele %s in layer %s', ele.id(), layer.id);
27888 self.drawEleInLayer(layer, ele, lvl, pxRatio);
27889 }
27890
27891 layer.eles.push(ele);
27892 caches[lvl] = layer;
27893 } // log('--');
27894
27895
27896 if (tmpLayers) {
27897 // then we only queued the current layerset and can't draw it yet
27898 return tmpLayers;
27899 }
27900
27901 if (allowLazyQueueing) {
27902 // log('lazy queue level', lvl);
27903 return null;
27904 }
27905
27906 return layers;
27907}; // a layer may want to use an ele cache of a higher level to avoid blurriness
27908// so the layer level might not equal the ele level
27909
27910
27911LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
27912 return lvl;
27913};
27914
27915LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
27916 var self = this;
27917 var r = this.renderer;
27918 var context = layer.context;
27919 var bb = ele.boundingBox();
27920
27921 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
27922 return;
27923 }
27924
27925 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
27926
27927 {
27928 r.setImgSmoothing(context, false);
27929 }
27930
27931 {
27932 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
27933 }
27934
27935 {
27936 r.setImgSmoothing(context, true);
27937 }
27938};
27939
27940LTCp.levelIsComplete = function (lvl, eles) {
27941 var self = this;
27942 var layers = self.layersByLevel[lvl];
27943
27944 if (!layers || layers.length === 0) {
27945 return false;
27946 }
27947
27948 var numElesInLayers = 0;
27949
27950 for (var i = 0; i < layers.length; i++) {
27951 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
27952
27953 if (layer.reqs > 0) {
27954 return false;
27955 } // if the layer is invalid, the level is not complete
27956
27957
27958 if (layer.invalid) {
27959 return false;
27960 }
27961
27962 numElesInLayers += layer.eles.length;
27963 } // we should have exactly the number of eles passed in to be complete
27964
27965
27966 if (numElesInLayers !== eles.length) {
27967 return false;
27968 }
27969
27970 return true;
27971};
27972
27973LTCp.validateLayersElesOrdering = function (lvl, eles) {
27974 var layers = this.layersByLevel[lvl];
27975
27976 if (!layers) {
27977 return;
27978 } // if in a layer the eles are not in the same order, then the layer is invalid
27979 // (i.e. there is an ele in between the eles in the layer)
27980
27981
27982 for (var i = 0; i < layers.length; i++) {
27983 var layer = layers[i];
27984 var offset = -1; // find the offset
27985
27986 for (var j = 0; j < eles.length; j++) {
27987 if (layer.eles[0] === eles[j]) {
27988 offset = j;
27989 break;
27990 }
27991 }
27992
27993 if (offset < 0) {
27994 // then the layer has nonexistant elements and is invalid
27995 this.invalidateLayer(layer);
27996 continue;
27997 } // the eles in the layer must be in the same continuous order, else the layer is invalid
27998
27999
28000 var o = offset;
28001
28002 for (var j = 0; j < layer.eles.length; j++) {
28003 if (layer.eles[j] !== eles[o + j]) {
28004 // log('invalidate based on ordering', layer.id);
28005 this.invalidateLayer(layer);
28006 break;
28007 }
28008 }
28009 }
28010};
28011
28012LTCp.updateElementsInLayers = function (eles, update) {
28013 var self = this;
28014 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28015 // layer itself along the way
28016
28017 for (var i = 0; i < eles.length; i++) {
28018 var req = isEles ? null : eles[i];
28019 var ele = isEles ? eles[i] : eles[i].ele;
28020 var rs = ele._private.rscratch;
28021 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28022
28023 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28024 var layer = caches[l];
28025
28026 if (!layer) {
28027 continue;
28028 } // if update is a request from the ele cache, then it affects only
28029 // the matching level
28030
28031
28032 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28033 continue;
28034 }
28035
28036 update(layer, ele, req);
28037 }
28038 }
28039};
28040
28041LTCp.haveLayers = function () {
28042 var self = this;
28043 var haveLayers = false;
28044
28045 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28046 var layers = self.layersByLevel[l];
28047
28048 if (layers && layers.length > 0) {
28049 haveLayers = true;
28050 break;
28051 }
28052 }
28053
28054 return haveLayers;
28055};
28056
28057LTCp.invalidateElements = function (eles) {
28058 var self = this;
28059
28060 if (eles.length === 0) {
28061 return;
28062 }
28063
28064 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28065
28066 if (eles.length === 0 || !self.haveLayers()) {
28067 return;
28068 }
28069
28070 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28071 self.invalidateLayer(layer);
28072 });
28073};
28074
28075LTCp.invalidateLayer = function (layer) {
28076 // log('update invalidate layer time');
28077 this.lastInvalidationTime = performanceNow();
28078
28079 if (layer.invalid) {
28080 return;
28081 } // save cycles
28082
28083
28084 var lvl = layer.level;
28085 var eles = layer.eles;
28086 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28087
28088 removeFromArray(layers, layer); // layer.eles = [];
28089
28090 layer.elesQueue = [];
28091 layer.invalid = true;
28092
28093 if (layer.replacement) {
28094 layer.replacement.invalid = true;
28095 }
28096
28097 for (var i = 0; i < eles.length; i++) {
28098 var caches = eles[i]._private.rscratch.imgLayerCaches;
28099
28100 if (caches) {
28101 caches[lvl] = null;
28102 }
28103 }
28104};
28105
28106LTCp.refineElementTextures = function (eles) {
28107 var self = this; // log('refine', eles.length);
28108
28109 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28110 var rLyr = layer.replacement;
28111
28112 if (!rLyr) {
28113 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28114 rLyr.replaces = layer;
28115 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28116 }
28117
28118 if (!rLyr.reqs) {
28119 for (var i = 0; i < rLyr.eles.length; i++) {
28120 self.queueLayer(rLyr, rLyr.eles[i]);
28121 } // log('queue replacement layer refinement', rLyr.id);
28122
28123 }
28124 });
28125};
28126
28127LTCp.enqueueElementRefinement = function (ele) {
28128
28129 this.eleTxrDeqs.merge(ele);
28130 this.scheduleElementRefinement();
28131};
28132
28133LTCp.queueLayer = function (layer, ele) {
28134 var self = this;
28135 var q = self.layersQueue;
28136 var elesQ = layer.elesQueue;
28137 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28138
28139 if (layer.replacement) {
28140 return;
28141 }
28142
28143 if (ele) {
28144 if (hasId[ele.id()]) {
28145 return;
28146 }
28147
28148 elesQ.push(ele);
28149 hasId[ele.id()] = true;
28150 }
28151
28152 if (layer.reqs) {
28153 layer.reqs++;
28154 q.updateItem(layer);
28155 } else {
28156 layer.reqs = 1;
28157 q.push(layer);
28158 }
28159};
28160
28161LTCp.dequeue = function (pxRatio) {
28162 var self = this;
28163 var q = self.layersQueue;
28164 var deqd = [];
28165 var eleDeqs = 0;
28166
28167 while (eleDeqs < maxDeqSize$1) {
28168 if (q.size() === 0) {
28169 break;
28170 }
28171
28172 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28173
28174 if (layer.replacement) {
28175 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28176 q.pop();
28177 continue;
28178 } // if this is a replacement layer that has been superceded, then forget it
28179
28180
28181 if (layer.replaces && layer !== layer.replaces.replacement) {
28182 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28183 q.pop();
28184 continue;
28185 }
28186
28187 if (layer.invalid) {
28188 // log('replacement layer %s is invalid; dequeued', layer.id);
28189 q.pop();
28190 continue;
28191 }
28192
28193 var ele = layer.elesQueue.shift();
28194
28195 if (ele) {
28196 // log('dequeue layer %s', layer.id);
28197 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28198 eleDeqs++;
28199 }
28200
28201 if (deqd.length === 0) {
28202 // we need only one entry in deqd to queue redrawing etc
28203 deqd.push(true);
28204 } // if the layer has all its eles done, then remove from the queue
28205
28206
28207 if (layer.elesQueue.length === 0) {
28208 q.pop();
28209 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28210 // when a replacement layer is dequeued, it replaces the old layer in the level
28211
28212 if (layer.replaces) {
28213 self.applyLayerReplacement(layer);
28214 }
28215
28216 self.requestRedraw();
28217 }
28218 }
28219
28220 return deqd;
28221};
28222
28223LTCp.applyLayerReplacement = function (layer) {
28224 var self = this;
28225 var layersInLevel = self.layersByLevel[layer.level];
28226 var replaced = layer.replaces;
28227 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28228 // refs would be a mistake (i.e. overwriting the true active layer)
28229
28230 if (index < 0 || replaced.invalid) {
28231 // log('replacement layer would have no effect', layer.id);
28232 return;
28233 }
28234
28235 layersInLevel[index] = layer; // replace level ref
28236 // replace refs in eles
28237
28238 for (var i = 0; i < layer.eles.length; i++) {
28239 var _p = layer.eles[i]._private;
28240 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28241
28242 if (cache) {
28243 cache[layer.level] = layer;
28244 }
28245 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
28246
28247
28248 self.requestRedraw();
28249};
28250
28251LTCp.requestRedraw = util(function () {
28252 var r = this.renderer;
28253 r.redrawHint('eles', true);
28254 r.redrawHint('drag', true);
28255 r.redraw();
28256}, 100);
28257LTCp.setupDequeueing = defs.setupDequeueing({
28258 deqRedrawThreshold: deqRedrawThreshold$1,
28259 deqCost: deqCost$1,
28260 deqAvgCost: deqAvgCost$1,
28261 deqNoDrawCost: deqNoDrawCost$1,
28262 deqFastCost: deqFastCost$1,
28263 deq: function deq(self, pxRatio) {
28264 return self.dequeue(pxRatio);
28265 },
28266 onDeqd: noop,
28267 shouldRedraw: trueify,
28268 priority: function priority(self) {
28269 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28270 }
28271});
28272
28273var CRp = {};
28274var impl;
28275
28276function polygon(context, points) {
28277 for (var i = 0; i < points.length; i++) {
28278 var pt = points[i];
28279 context.lineTo(pt.x, pt.y);
28280 }
28281}
28282
28283function triangleBackcurve(context, points, controlPoint) {
28284 var firstPt;
28285
28286 for (var i = 0; i < points.length; i++) {
28287 var pt = points[i];
28288
28289 if (i === 0) {
28290 firstPt = pt;
28291 }
28292
28293 context.lineTo(pt.x, pt.y);
28294 }
28295
28296 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28297}
28298
28299function triangleTee(context, trianglePoints, teePoints) {
28300 if (context.beginPath) {
28301 context.beginPath();
28302 }
28303
28304 var triPts = trianglePoints;
28305
28306 for (var i = 0; i < triPts.length; i++) {
28307 var pt = triPts[i];
28308 context.lineTo(pt.x, pt.y);
28309 }
28310
28311 var teePts = teePoints;
28312 var firstTeePt = teePoints[0];
28313 context.moveTo(firstTeePt.x, firstTeePt.y);
28314
28315 for (var i = 1; i < teePts.length; i++) {
28316 var pt = teePts[i];
28317 context.lineTo(pt.x, pt.y);
28318 }
28319
28320 if (context.closePath) {
28321 context.closePath();
28322 }
28323}
28324
28325function circle(context, rx, ry, r) {
28326 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28327}
28328
28329CRp.arrowShapeImpl = function (name) {
28330 return (impl || (impl = {
28331 'polygon': polygon,
28332 'triangle-backcurve': triangleBackcurve,
28333 'triangle-tee': triangleTee,
28334 'triangle-cross': triangleTee,
28335 'circle': circle
28336 }))[name];
28337};
28338
28339var CRp$1 = {};
28340
28341CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28342 var r = this;
28343
28344 if (ele.isNode()) {
28345 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28346 } else {
28347 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28348 }
28349};
28350
28351CRp$1.drawElementOverlay = function (context, ele) {
28352 var r = this;
28353
28354 if (ele.isNode()) {
28355 r.drawNodeOverlay(context, ele);
28356 } else {
28357 r.drawEdgeOverlay(context, ele);
28358 }
28359};
28360
28361CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28362 var r = this;
28363 var bb = eleTxrCache.getBoundingBox(ele);
28364
28365 if (bb.w === 0 || bb.h === 0) {
28366 return;
28367 } // ignore zero size case
28368
28369
28370 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28371
28372 if (eleCache != null) {
28373 var opacity = getOpacity(r, ele);
28374
28375 if (opacity === 0) {
28376 return;
28377 }
28378
28379 var theta = getRotation(r, ele);
28380 var x1 = bb.x1,
28381 y1 = bb.y1,
28382 w = bb.w,
28383 h = bb.h;
28384 var x, y, sx, sy, smooth;
28385
28386 if (theta !== 0) {
28387 var rotPt = eleTxrCache.getRotationPoint(ele);
28388 sx = rotPt.x;
28389 sy = rotPt.y;
28390 context.translate(sx, sy);
28391 context.rotate(theta);
28392 smooth = r.getImgSmoothing(context);
28393
28394 if (!smooth) {
28395 r.setImgSmoothing(context, true);
28396 }
28397
28398 var off = eleTxrCache.getRotationOffset(ele);
28399 x = off.x;
28400 y = off.y;
28401 } else {
28402 x = x1;
28403 y = y1;
28404 }
28405
28406 var oldGlobalAlpha;
28407
28408 if (opacity !== 1) {
28409 oldGlobalAlpha = context.globalAlpha;
28410 context.globalAlpha = oldGlobalAlpha * opacity;
28411 }
28412
28413 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28414
28415 if (opacity !== 1) {
28416 context.globalAlpha = oldGlobalAlpha;
28417 }
28418
28419 if (theta !== 0) {
28420 context.rotate(-theta);
28421 context.translate(-sx, -sy);
28422
28423 if (!smooth) {
28424 r.setImgSmoothing(context, false);
28425 }
28426 }
28427 } else {
28428 eleTxrCache.drawElement(context, ele); // direct draw fallback
28429 }
28430};
28431
28432var getZeroRotation = function getZeroRotation() {
28433 return 0;
28434};
28435
28436var getLabelRotation = function getLabelRotation(r, ele) {
28437 return r.getTextAngle(ele, null);
28438};
28439
28440var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28441 return r.getTextAngle(ele, 'source');
28442};
28443
28444var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28445 return r.getTextAngle(ele, 'target');
28446};
28447
28448var getOpacity = function getOpacity(r, ele) {
28449 return ele.effectiveOpacity();
28450};
28451
28452var getTextOpacity = function getTextOpacity(e, ele) {
28453 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28454};
28455
28456CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28457 var r = this;
28458 var _r$data = r.data,
28459 eleTxrCache = _r$data.eleTxrCache,
28460 lblTxrCache = _r$data.lblTxrCache,
28461 slbTxrCache = _r$data.slbTxrCache,
28462 tlbTxrCache = _r$data.tlbTxrCache;
28463 var bb = ele.boundingBox();
28464 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28465
28466 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28467 return;
28468 }
28469
28470 if (!extent || boundingBoxesIntersect(bb, extent)) {
28471 var isEdge = ele.isEdge();
28472
28473 var badLine = ele.element()._private.rscratch.badLine;
28474
28475 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28476
28477 if (!isEdge || !badLine) {
28478 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28479 }
28480
28481 if (isEdge && !badLine) {
28482 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28483 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28484 }
28485
28486 r.drawElementOverlay(context, ele);
28487 }
28488};
28489
28490CRp$1.drawElements = function (context, eles) {
28491 var r = this;
28492
28493 for (var i = 0; i < eles.length; i++) {
28494 var ele = eles[i];
28495 r.drawElement(context, ele);
28496 }
28497};
28498
28499CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
28500 var r = this;
28501
28502 for (var i = 0; i < eles.length; i++) {
28503 var ele = eles[i];
28504 r.drawCachedElement(context, ele, pxRatio, extent);
28505 }
28506};
28507
28508CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
28509 var r = this;
28510
28511 for (var i = 0; i < eles.length; i++) {
28512 var ele = eles[i];
28513
28514 if (!ele.isNode()) {
28515 continue;
28516 }
28517
28518 r.drawCachedElement(context, ele, pxRatio, extent);
28519 }
28520};
28521
28522CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
28523 var r = this;
28524 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28525
28526 if (layers) {
28527 for (var i = 0; i < layers.length; i++) {
28528 var layer = layers[i];
28529 var bb = layer.bb;
28530
28531 if (bb.w === 0 || bb.h === 0) {
28532 continue;
28533 }
28534
28535 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28536 }
28537 } else {
28538 // fall back on plain caching if no layers
28539 r.drawCachedElements(context, eles, pxRatio, extent);
28540 }
28541};
28542
28543/* global Path2D */
28544var CRp$2 = {};
28545
28546CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
28547 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28548 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28549 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28550 var r = this;
28551 var rs = edge._private.rscratch;
28552
28553 if (shouldDrawOpacity && !edge.visible()) {
28554 return;
28555 } // if bezier ctrl pts can not be calculated, then die
28556
28557
28558 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28559 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28560 return;
28561 }
28562
28563 var bb;
28564
28565 if (shiftToOriginWithBb) {
28566 bb = shiftToOriginWithBb;
28567 context.translate(-bb.x1, -bb.y1);
28568 }
28569
28570 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28571 var lineStyle = edge.pstyle('line-style').value;
28572 var edgeWidth = edge.pstyle('width').pfValue;
28573 var lineCap = edge.pstyle('line-cap').value;
28574
28575 var drawLine = function drawLine() {
28576 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
28577 context.lineWidth = edgeWidth;
28578 context.lineCap = lineCap;
28579 r.eleStrokeStyle(context, edge, strokeOpacity);
28580 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28581 context.lineCap = 'butt'; // reset for other drawing functions
28582 };
28583
28584 var drawOverlay = function drawOverlay() {
28585 if (!shouldDrawOverlay) {
28586 return;
28587 }
28588
28589 r.drawEdgeOverlay(context, edge);
28590 };
28591
28592 var drawArrows = function drawArrows() {
28593 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
28594 r.drawArrowheads(context, edge, arrowOpacity);
28595 };
28596
28597 var drawText = function drawText() {
28598 r.drawElementText(context, edge, null, drawLabel);
28599 };
28600
28601 context.lineJoin = 'round';
28602 var ghost = edge.pstyle('ghost').value === 'yes';
28603
28604 if (ghost) {
28605 var gx = edge.pstyle('ghost-offset-x').pfValue;
28606 var gy = edge.pstyle('ghost-offset-y').pfValue;
28607 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28608 var effectiveGhostOpacity = opacity * ghostOpacity;
28609 context.translate(gx, gy);
28610 drawLine(effectiveGhostOpacity);
28611 drawArrows(effectiveGhostOpacity);
28612 context.translate(-gx, -gy);
28613 }
28614
28615 drawLine();
28616 drawArrows();
28617 drawOverlay();
28618 drawText();
28619
28620 if (shiftToOriginWithBb) {
28621 context.translate(bb.x1, bb.y1);
28622 }
28623};
28624
28625CRp$2.drawEdgeOverlay = function (context, edge) {
28626 if (!edge.visible()) {
28627 return;
28628 }
28629
28630 var overlayOpacity = edge.pstyle('overlay-opacity').value;
28631
28632 if (overlayOpacity === 0) {
28633 return;
28634 }
28635
28636 var r = this;
28637 var usePaths = r.usePaths();
28638 var rs = edge._private.rscratch;
28639 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
28640 var overlayWidth = 2 * overlayPadding;
28641 var overlayColor = edge.pstyle('overlay-color').value;
28642 context.lineWidth = overlayWidth;
28643
28644 if (rs.edgeType === 'self' && !usePaths) {
28645 context.lineCap = 'butt';
28646 } else {
28647 context.lineCap = 'round';
28648 }
28649
28650 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
28651 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28652};
28653
28654CRp$2.drawEdgePath = function (edge, context, pts, type) {
28655 var rs = edge._private.rscratch;
28656 var canvasCxt = context;
28657 var path;
28658 var pathCacheHit = false;
28659 var usePaths = this.usePaths();
28660 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28661 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28662
28663 if (usePaths) {
28664 var pathCacheKey = pts.join('$');
28665 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28666
28667 if (keyMatches) {
28668 path = context = rs.pathCache;
28669 pathCacheHit = true;
28670 } else {
28671 path = context = new Path2D();
28672 rs.pathCacheKey = pathCacheKey;
28673 rs.pathCache = path;
28674 }
28675 }
28676
28677 if (canvasCxt.setLineDash) {
28678 // for very outofdate browsers
28679 switch (type) {
28680 case 'dotted':
28681 canvasCxt.setLineDash([1, 1]);
28682 break;
28683
28684 case 'dashed':
28685 canvasCxt.setLineDash(lineDashPattern);
28686 canvasCxt.lineDashOffset = lineDashOffset;
28687 break;
28688
28689 case 'solid':
28690 canvasCxt.setLineDash([]);
28691 break;
28692 }
28693 }
28694
28695 if (!pathCacheHit && !rs.badLine) {
28696 if (context.beginPath) {
28697 context.beginPath();
28698 }
28699
28700 context.moveTo(pts[0], pts[1]);
28701
28702 switch (rs.edgeType) {
28703 case 'bezier':
28704 case 'self':
28705 case 'compound':
28706 case 'multibezier':
28707 for (var i = 2; i + 3 < pts.length; i += 4) {
28708 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
28709 }
28710
28711 break;
28712
28713 case 'straight':
28714 case 'segments':
28715 case 'haystack':
28716 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
28717 context.lineTo(pts[_i], pts[_i + 1]);
28718 }
28719
28720 break;
28721 }
28722 }
28723
28724 context = canvasCxt;
28725
28726 if (usePaths) {
28727 context.stroke(path);
28728 } else {
28729 context.stroke();
28730 } // reset any line dashes
28731
28732
28733 if (context.setLineDash) {
28734 // for very outofdate browsers
28735 context.setLineDash([]);
28736 }
28737};
28738
28739CRp$2.drawArrowheads = function (context, edge, opacity) {
28740 var rs = edge._private.rscratch;
28741 var isHaystack = rs.edgeType === 'haystack';
28742
28743 if (!isHaystack) {
28744 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
28745 }
28746
28747 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
28748 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
28749
28750 if (!isHaystack) {
28751 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
28752 }
28753};
28754
28755CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
28756 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
28757 return;
28758 }
28759
28760 var self = this;
28761 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
28762
28763 if (arrowShape === 'none') {
28764 return;
28765 }
28766
28767 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
28768 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
28769 var edgeWidth = edge.pstyle('width').pfValue;
28770 var edgeOpacity = edge.pstyle('opacity').value;
28771
28772 if (opacity === undefined) {
28773 opacity = edgeOpacity;
28774 }
28775
28776 var gco = context.globalCompositeOperation;
28777
28778 if (opacity !== 1 || arrowFill === 'hollow') {
28779 // then extra clear is needed
28780 context.globalCompositeOperation = 'destination-out';
28781 self.colorFillStyle(context, 255, 255, 255, 1);
28782 self.colorStrokeStyle(context, 255, 255, 255, 1);
28783 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
28784 context.globalCompositeOperation = gco;
28785 } // otherwise, the opaque arrow clears it for free :)
28786
28787
28788 var color = edge.pstyle(prefix + '-arrow-color').value;
28789 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
28790 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28791 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
28792};
28793
28794CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
28795 var r = this;
28796 var usePaths = this.usePaths() && shape !== 'triangle-cross';
28797 var pathCacheHit = false;
28798 var path;
28799 var canvasContext = context;
28800 var translation = {
28801 x: x,
28802 y: y
28803 };
28804 var scale = edge.pstyle('arrow-scale').value;
28805 var size = this.getArrowWidth(edgeWidth, scale);
28806 var shapeImpl = r.arrowShapes[shape];
28807
28808 if (usePaths) {
28809 var cache = r.arrowPathCache = r.arrowPathCache || [];
28810 var key = hashString(shape);
28811 var cachedPath = cache[key];
28812
28813 if (cachedPath != null) {
28814 path = context = cachedPath;
28815 pathCacheHit = true;
28816 } else {
28817 path = context = new Path2D();
28818 cache[key] = path;
28819 }
28820 }
28821
28822 if (!pathCacheHit) {
28823 if (context.beginPath) {
28824 context.beginPath();
28825 }
28826
28827 if (usePaths) {
28828 // store in the path cache with values easily manipulated later
28829 shapeImpl.draw(context, 1, 0, {
28830 x: 0,
28831 y: 0
28832 }, 1);
28833 } else {
28834 shapeImpl.draw(context, size, angle, translation, edgeWidth);
28835 }
28836
28837 if (context.closePath) {
28838 context.closePath();
28839 }
28840 }
28841
28842 context = canvasContext;
28843
28844 if (usePaths) {
28845 // set transform to arrow position/orientation
28846 context.translate(x, y);
28847 context.rotate(angle);
28848 context.scale(size, size);
28849 }
28850
28851 if (fill === 'filled' || fill === 'both') {
28852 if (usePaths) {
28853 context.fill(path);
28854 } else {
28855 context.fill();
28856 }
28857 }
28858
28859 if (fill === 'hollow' || fill === 'both') {
28860 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
28861 context.lineJoin = 'miter';
28862
28863 if (usePaths) {
28864 context.stroke(path);
28865 } else {
28866 context.stroke();
28867 }
28868 }
28869
28870 if (usePaths) {
28871 // reset transform by applying inverse
28872 context.scale(1 / size, 1 / size);
28873 context.rotate(-angle);
28874 context.translate(-x, -y);
28875 }
28876};
28877
28878var CRp$3 = {};
28879
28880CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
28881 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
28882 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
28883 return;
28884 }
28885
28886 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
28887};
28888
28889CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
28890 var r = this;
28891 var pos = node.position();
28892 var nodeX = pos.x;
28893 var nodeY = pos.y;
28894 var styleObj = node.cy().style();
28895 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
28896 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
28897 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
28898 var nodeW = node.width();
28899 var nodeH = node.height();
28900 var paddingX2 = node.padding() * 2;
28901 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28902 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28903 var rs = node._private.rscratch;
28904 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
28905 var shouldClip = clip === 'node';
28906 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
28907 var imgW = img.width || img.cachedW;
28908 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
28909
28910 if (null == imgW || null == imgH) {
28911 document.body.appendChild(img); // eslint-disable-line no-undef
28912
28913 imgW = img.cachedW = img.width || img.offsetWidth;
28914 imgH = img.cachedH = img.height || img.offsetHeight;
28915 document.body.removeChild(img); // eslint-disable-line no-undef
28916 }
28917
28918 var w = imgW;
28919 var h = imgH;
28920
28921 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
28922 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
28923 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
28924 } else {
28925 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
28926 }
28927 }
28928
28929 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
28930 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
28931 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
28932 } else {
28933 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
28934 }
28935 }
28936
28937 if (w === 0 || h === 0) {
28938 return; // no point in drawing empty image (and chrome is broken in this case)
28939 }
28940
28941 if (fit === 'contain') {
28942 var scale = Math.min(nodeTW / w, nodeTH / h);
28943 w *= scale;
28944 h *= scale;
28945 } else if (fit === 'cover') {
28946 var scale = Math.max(nodeTW / w, nodeTH / h);
28947 w *= scale;
28948 h *= scale;
28949 }
28950
28951 var x = nodeX - nodeTW / 2; // left
28952
28953 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
28954 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
28955
28956 if (posXUnits === '%') {
28957 x += (nodeTW - w) * posXPfVal;
28958 } else {
28959 x += posXPfVal;
28960 }
28961
28962 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
28963 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
28964
28965 if (offXUnits === '%') {
28966 x += (nodeTW - w) * offXPfVal;
28967 } else {
28968 x += offXPfVal;
28969 }
28970
28971 var y = nodeY - nodeTH / 2; // top
28972
28973 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
28974 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
28975
28976 if (posYUnits === '%') {
28977 y += (nodeTH - h) * posYPfVal;
28978 } else {
28979 y += posYPfVal;
28980 }
28981
28982 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
28983 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
28984
28985 if (offYUnits === '%') {
28986 y += (nodeTH - h) * offYPfVal;
28987 } else {
28988 y += offYPfVal;
28989 }
28990
28991 if (rs.pathCache) {
28992 x -= nodeX;
28993 y -= nodeY;
28994 nodeX = 0;
28995 nodeY = 0;
28996 }
28997
28998 var gAlpha = context.globalAlpha;
28999 context.globalAlpha = imgOpacity;
29000
29001 if (repeat === 'no-repeat') {
29002 if (shouldClip) {
29003 context.save();
29004
29005 if (rs.pathCache) {
29006 context.clip(rs.pathCache);
29007 } else {
29008 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29009 context.clip();
29010 }
29011 }
29012
29013 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29014
29015 if (shouldClip) {
29016 context.restore();
29017 }
29018 } else {
29019 var pattern = context.createPattern(img, repeat);
29020 context.fillStyle = pattern;
29021 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29022 context.translate(x, y);
29023 context.fill();
29024 context.translate(-x, -y);
29025 }
29026
29027 context.globalAlpha = gAlpha;
29028};
29029
29030var CRp$4 = {};
29031
29032CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29033 if (!scale) {
29034 var zoom = ele.cy().zoom();
29035 var pxRatio = this.getPixelRatio();
29036 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29037
29038 scale = Math.pow(2, lvl);
29039 }
29040
29041 var computedSize = ele.pstyle('font-size').pfValue * scale;
29042 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29043
29044 if (computedSize < minSize) {
29045 return false;
29046 }
29047
29048 return true;
29049};
29050
29051CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29052 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29053 var r = this;
29054
29055 if (force == null) {
29056 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29057 return;
29058 }
29059 } else if (force === false) {
29060 return;
29061 }
29062
29063 if (ele.isNode()) {
29064 var label = ele.pstyle('label');
29065
29066 if (!label || !label.value) {
29067 return;
29068 }
29069
29070 var justification = r.getLabelJustification(ele);
29071 context.textAlign = justification;
29072 context.textBaseline = 'bottom';
29073 } else {
29074 var badLine = ele.element()._private.rscratch.badLine;
29075
29076 var _label = ele.pstyle('label');
29077
29078 var srcLabel = ele.pstyle('source-label');
29079 var tgtLabel = ele.pstyle('target-label');
29080
29081 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29082 return;
29083 }
29084
29085 context.textAlign = 'center';
29086 context.textBaseline = 'bottom';
29087 }
29088
29089 var applyRotation = !shiftToOriginWithBb;
29090 var bb;
29091
29092 if (shiftToOriginWithBb) {
29093 bb = shiftToOriginWithBb;
29094 context.translate(-bb.x1, -bb.y1);
29095 }
29096
29097 if (prefix == null) {
29098 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29099
29100 if (ele.isEdge()) {
29101 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29102 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29103 }
29104 } else {
29105 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29106 }
29107
29108 if (shiftToOriginWithBb) {
29109 context.translate(bb.x1, bb.y1);
29110 }
29111};
29112
29113CRp$4.getFontCache = function (context) {
29114 var cache;
29115 this.fontCaches = this.fontCaches || [];
29116
29117 for (var i = 0; i < this.fontCaches.length; i++) {
29118 cache = this.fontCaches[i];
29119
29120 if (cache.context === context) {
29121 return cache;
29122 }
29123 }
29124
29125 cache = {
29126 context: context
29127 };
29128 this.fontCaches.push(cache);
29129 return cache;
29130}; // set up canvas context with font
29131// returns transformed text string
29132
29133
29134CRp$4.setupTextStyle = function (context, ele) {
29135 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29136 // Font style
29137 var labelStyle = ele.pstyle('font-style').strValue;
29138 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29139 var labelFamily = ele.pstyle('font-family').strValue;
29140 var labelWeight = ele.pstyle('font-weight').strValue;
29141 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29142 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29143 var color = ele.pstyle('color').value;
29144 var outlineColor = ele.pstyle('text-outline-color').value;
29145 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29146 context.lineJoin = 'round'; // so text outlines aren't jagged
29147
29148 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29149 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29150}; // TODO ensure re-used
29151
29152
29153function roundRect(ctx, x, y, width, height) {
29154 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29155 ctx.beginPath();
29156 ctx.moveTo(x + radius, y);
29157 ctx.lineTo(x + width - radius, y);
29158 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29159 ctx.lineTo(x + width, y + height - radius);
29160 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29161 ctx.lineTo(x + radius, y + height);
29162 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29163 ctx.lineTo(x, y + radius);
29164 ctx.quadraticCurveTo(x, y, x + radius, y);
29165 ctx.closePath();
29166 ctx.fill();
29167}
29168
29169CRp$4.getTextAngle = function (ele, prefix) {
29170 var theta;
29171 var _p = ele._private;
29172 var rscratch = _p.rscratch;
29173 var pdash = prefix ? prefix + '-' : '';
29174 var rotation = ele.pstyle(pdash + 'text-rotation');
29175 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29176
29177 if (rotation.strValue === 'autorotate') {
29178 theta = ele.isEdge() ? textAngle : 0;
29179 } else if (rotation.strValue === 'none') {
29180 theta = 0;
29181 } else {
29182 theta = rotation.pfValue;
29183 }
29184
29185 return theta;
29186};
29187
29188CRp$4.drawText = function (context, ele, prefix) {
29189 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29190 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29191 var _p = ele._private;
29192 var rscratch = _p.rscratch;
29193 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29194
29195 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29196 return;
29197 } // use 'main' as an alias for the main label (i.e. null prefix)
29198
29199
29200 if (prefix === 'main') {
29201 prefix = null;
29202 }
29203
29204 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29205 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29206 var orgTextX, orgTextY; // used for rotation
29207
29208 var text = this.getLabelText(ele, prefix);
29209
29210 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29211 this.setupTextStyle(context, ele, useEleOpacity);
29212 var pdash = prefix ? prefix + '-' : '';
29213 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29214 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29215 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29216 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29217 var isEdge = ele.isEdge();
29218 var halign = ele.pstyle('text-halign').value;
29219 var valign = ele.pstyle('text-valign').value;
29220
29221 if (isEdge) {
29222 halign = 'center';
29223 valign = 'center';
29224 }
29225
29226 textX += marginX;
29227 textY += marginY;
29228 var theta;
29229
29230 if (!applyRotation) {
29231 theta = 0;
29232 } else {
29233 theta = this.getTextAngle(ele, prefix);
29234 }
29235
29236 if (theta !== 0) {
29237 orgTextX = textX;
29238 orgTextY = textY;
29239 context.translate(orgTextX, orgTextY);
29240 context.rotate(theta);
29241 textX = 0;
29242 textY = 0;
29243 }
29244
29245 switch (valign) {
29246 case 'top':
29247 break;
29248
29249 case 'center':
29250 textY += textH / 2;
29251 break;
29252
29253 case 'bottom':
29254 textY += textH;
29255 break;
29256 }
29257
29258 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29259 var borderOpacity = ele.pstyle('text-border-opacity').value;
29260 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29261 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29262
29263 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29264 var bgX = textX - backgroundPadding;
29265
29266 switch (halign) {
29267 case 'left':
29268 bgX -= textW;
29269 break;
29270
29271 case 'center':
29272 bgX -= textW / 2;
29273 break;
29274 }
29275
29276 var bgY = textY - textH - backgroundPadding;
29277 var bgW = textW + 2 * backgroundPadding;
29278 var bgH = textH + 2 * backgroundPadding;
29279
29280 if (backgroundOpacity > 0) {
29281 var textFill = context.fillStyle;
29282 var textBackgroundColor = ele.pstyle('text-background-color').value;
29283 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29284 var styleShape = ele.pstyle('text-background-shape').strValue;
29285
29286 if (styleShape.indexOf('round') === 0) {
29287 roundRect(context, bgX, bgY, bgW, bgH, 2);
29288 } else {
29289 context.fillRect(bgX, bgY, bgW, bgH);
29290 }
29291
29292 context.fillStyle = textFill;
29293 }
29294
29295 if (textBorderWidth > 0 && borderOpacity > 0) {
29296 var textStroke = context.strokeStyle;
29297 var textLineWidth = context.lineWidth;
29298 var textBorderColor = ele.pstyle('text-border-color').value;
29299 var textBorderStyle = ele.pstyle('text-border-style').value;
29300 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29301 context.lineWidth = textBorderWidth;
29302
29303 if (context.setLineDash) {
29304 // for very outofdate browsers
29305 switch (textBorderStyle) {
29306 case 'dotted':
29307 context.setLineDash([1, 1]);
29308 break;
29309
29310 case 'dashed':
29311 context.setLineDash([4, 2]);
29312 break;
29313
29314 case 'double':
29315 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29316
29317 context.setLineDash([]);
29318 break;
29319
29320 case 'solid':
29321 context.setLineDash([]);
29322 break;
29323 }
29324 }
29325
29326 context.strokeRect(bgX, bgY, bgW, bgH);
29327
29328 if (textBorderStyle === 'double') {
29329 var whiteWidth = textBorderWidth / 2;
29330 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29331 }
29332
29333 if (context.setLineDash) {
29334 // for very outofdate browsers
29335 context.setLineDash([]);
29336 }
29337
29338 context.lineWidth = textLineWidth;
29339 context.strokeStyle = textStroke;
29340 }
29341 }
29342
29343 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29344
29345 if (lineWidth > 0) {
29346 context.lineWidth = lineWidth;
29347 }
29348
29349 if (ele.pstyle('text-wrap').value === 'wrap') {
29350 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29351 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29352 var halfTextW = textW / 2;
29353 var justification = this.getLabelJustification(ele);
29354
29355 if (justification === 'auto') ; else if (halign === 'left') {
29356 // auto justification : right
29357 if (justification === 'left') {
29358 textX += -textW;
29359 } else if (justification === 'center') {
29360 textX += -halfTextW;
29361 } // else same as auto
29362
29363 } else if (halign === 'center') {
29364 // auto justfication : center
29365 if (justification === 'left') {
29366 textX += -halfTextW;
29367 } else if (justification === 'right') {
29368 textX += halfTextW;
29369 } // else same as auto
29370
29371 } else if (halign === 'right') {
29372 // auto justification : left
29373 if (justification === 'center') {
29374 textX += halfTextW;
29375 } else if (justification === 'right') {
29376 textX += textW;
29377 } // else same as auto
29378
29379 }
29380
29381 switch (valign) {
29382 case 'top':
29383 textY -= (lines.length - 1) * lineHeight;
29384 break;
29385
29386 case 'center':
29387 case 'bottom':
29388 textY -= (lines.length - 1) * lineHeight;
29389 break;
29390 }
29391
29392 for (var l = 0; l < lines.length; l++) {
29393 if (lineWidth > 0) {
29394 context.strokeText(lines[l], textX, textY);
29395 }
29396
29397 context.fillText(lines[l], textX, textY);
29398 textY += lineHeight;
29399 }
29400 } else {
29401 if (lineWidth > 0) {
29402 context.strokeText(text, textX, textY);
29403 }
29404
29405 context.fillText(text, textX, textY);
29406 }
29407
29408 if (theta !== 0) {
29409 context.rotate(-theta);
29410 context.translate(-orgTextX, -orgTextY);
29411 }
29412 }
29413};
29414
29415/* global Path2D */
29416var CRp$5 = {};
29417
29418CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29419 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29420 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29421 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29422 var r = this;
29423 var nodeWidth, nodeHeight;
29424 var _p = node._private;
29425 var rs = _p.rscratch;
29426 var pos = node.position();
29427
29428 if (!number(pos.x) || !number(pos.y)) {
29429 return; // can't draw node with undefined position
29430 }
29431
29432 if (shouldDrawOpacity && !node.visible()) {
29433 return;
29434 }
29435
29436 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29437 var usePaths = r.usePaths();
29438 var path;
29439 var pathCacheHit = false;
29440 var padding = node.padding();
29441 nodeWidth = node.width() + 2 * padding;
29442 nodeHeight = node.height() + 2 * padding; //
29443 // setup shift
29444
29445 var bb;
29446
29447 if (shiftToOriginWithBb) {
29448 bb = shiftToOriginWithBb;
29449 context.translate(-bb.x1, -bb.y1);
29450 } //
29451 // load bg image
29452
29453
29454 var bgImgProp = node.pstyle('background-image');
29455 var urls = bgImgProp.value;
29456 var urlDefined = new Array(urls.length);
29457 var image = new Array(urls.length);
29458 var numImages = 0;
29459
29460 for (var i = 0; i < urls.length; i++) {
29461 var url = urls[i];
29462 var defd = urlDefined[i] = url != null && url !== 'none';
29463
29464 if (defd) {
29465 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29466 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29467
29468 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29469 _p.backgroundTimestamp = Date.now();
29470 node.emitAndNotify('background');
29471 });
29472 }
29473 } //
29474 // setup styles
29475
29476
29477 var darkness = node.pstyle('background-blacken').value;
29478 var borderWidth = node.pstyle('border-width').pfValue;
29479 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29480 var borderColor = node.pstyle('border-color').value;
29481 var borderStyle = node.pstyle('border-style').value;
29482 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29483 context.lineJoin = 'miter'; // so borders are square with the node shape
29484
29485 var setupShapeColor = function setupShapeColor() {
29486 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29487 r.eleFillStyle(context, node, bgOpy);
29488 };
29489
29490 var setupBorderColor = function setupBorderColor() {
29491 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29492 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29493 }; //
29494 // setup shape
29495
29496
29497 var styleShape = node.pstyle('shape').strValue;
29498 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29499
29500 if (usePaths) {
29501 context.translate(pos.x, pos.y);
29502 var pathCache = r.nodePathCache = r.nodePathCache || [];
29503 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29504 var cachedPath = pathCache[key];
29505
29506 if (cachedPath != null) {
29507 path = cachedPath;
29508 pathCacheHit = true;
29509 rs.pathCache = path;
29510 } else {
29511 path = new Path2D();
29512 pathCache[key] = rs.pathCache = path;
29513 }
29514 }
29515
29516 var drawShape = function drawShape() {
29517 if (!pathCacheHit) {
29518 var npos = pos;
29519
29520 if (usePaths) {
29521 npos = {
29522 x: 0,
29523 y: 0
29524 };
29525 }
29526
29527 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29528 }
29529
29530 if (usePaths) {
29531 context.fill(path);
29532 } else {
29533 context.fill();
29534 }
29535 };
29536
29537 var drawImages = function drawImages() {
29538 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29539 var prevBging = _p.backgrounding;
29540 var totalCompleted = 0;
29541
29542 for (var _i = 0; _i < image.length; _i++) {
29543 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29544 totalCompleted++;
29545 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29546 }
29547 }
29548
29549 _p.backgrounding = !(totalCompleted === numImages);
29550
29551 if (prevBging !== _p.backgrounding) {
29552 // update style b/c :backgrounding state changed
29553 node.updateStyle(false);
29554 }
29555 };
29556
29557 var drawPie = function drawPie() {
29558 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29559 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29560
29561 if (r.hasPie(node)) {
29562 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29563
29564 if (redrawShape) {
29565 if (!usePaths) {
29566 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29567 }
29568 }
29569 }
29570 };
29571
29572 var darken = function darken() {
29573 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29574 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29575 var c = darkness > 0 ? 0 : 255;
29576
29577 if (darkness !== 0) {
29578 r.colorFillStyle(context, c, c, c, opacity);
29579
29580 if (usePaths) {
29581 context.fill(path);
29582 } else {
29583 context.fill();
29584 }
29585 }
29586 };
29587
29588 var drawBorder = function drawBorder() {
29589 if (borderWidth > 0) {
29590 context.lineWidth = borderWidth;
29591 context.lineCap = 'butt';
29592
29593 if (context.setLineDash) {
29594 // for very outofdate browsers
29595 switch (borderStyle) {
29596 case 'dotted':
29597 context.setLineDash([1, 1]);
29598 break;
29599
29600 case 'dashed':
29601 context.setLineDash([4, 2]);
29602 break;
29603
29604 case 'solid':
29605 case 'double':
29606 context.setLineDash([]);
29607 break;
29608 }
29609 }
29610
29611 if (usePaths) {
29612 context.stroke(path);
29613 } else {
29614 context.stroke();
29615 }
29616
29617 if (borderStyle === 'double') {
29618 context.lineWidth = borderWidth / 3;
29619 var gco = context.globalCompositeOperation;
29620 context.globalCompositeOperation = 'destination-out';
29621
29622 if (usePaths) {
29623 context.stroke(path);
29624 } else {
29625 context.stroke();
29626 }
29627
29628 context.globalCompositeOperation = gco;
29629 } // reset in case we changed the border style
29630
29631
29632 if (context.setLineDash) {
29633 // for very outofdate browsers
29634 context.setLineDash([]);
29635 }
29636 }
29637 };
29638
29639 var drawOverlay = function drawOverlay() {
29640 if (shouldDrawOverlay) {
29641 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
29642 }
29643 };
29644
29645 var drawText = function drawText() {
29646 r.drawElementText(context, node, null, drawLabel);
29647 };
29648
29649 var ghost = node.pstyle('ghost').value === 'yes';
29650
29651 if (ghost) {
29652 var gx = node.pstyle('ghost-offset-x').pfValue;
29653 var gy = node.pstyle('ghost-offset-y').pfValue;
29654 var ghostOpacity = node.pstyle('ghost-opacity').value;
29655 var effGhostOpacity = ghostOpacity * eleOpacity;
29656 context.translate(gx, gy);
29657 setupShapeColor(ghostOpacity * bgOpacity);
29658 drawShape();
29659 drawImages(effGhostOpacity);
29660 drawPie(darkness !== 0 || borderWidth !== 0);
29661 darken(effGhostOpacity);
29662 setupBorderColor(ghostOpacity * borderOpacity);
29663 drawBorder();
29664 context.translate(-gx, -gy);
29665 }
29666
29667 setupShapeColor();
29668 drawShape();
29669 drawImages();
29670 drawPie(darkness !== 0 || borderWidth !== 0);
29671 darken();
29672 setupBorderColor();
29673 drawBorder();
29674
29675 if (usePaths) {
29676 context.translate(-pos.x, -pos.y);
29677 }
29678
29679 drawText();
29680 drawOverlay(); //
29681 // clean up shift
29682
29683 if (shiftToOriginWithBb) {
29684 context.translate(bb.x1, bb.y1);
29685 }
29686};
29687
29688CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
29689 var r = this;
29690
29691 if (!node.visible()) {
29692 return;
29693 }
29694
29695 var overlayPadding = node.pstyle('overlay-padding').pfValue;
29696 var overlayOpacity = node.pstyle('overlay-opacity').value;
29697 var overlayColor = node.pstyle('overlay-color').value;
29698
29699 if (overlayOpacity > 0) {
29700 pos = pos || node.position();
29701
29702 if (nodeWidth == null || nodeHeight == null) {
29703 var padding = node.padding();
29704 nodeWidth = node.width() + 2 * padding;
29705 nodeHeight = node.height() + 2 * padding;
29706 }
29707
29708 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29709 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
29710 context.fill();
29711 }
29712}; // does the node have at least one pie piece?
29713
29714
29715CRp$5.hasPie = function (node) {
29716 node = node[0]; // ensure ele ref
29717
29718 return node._private.hasPie;
29719};
29720
29721CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
29722 node = node[0]; // ensure ele ref
29723
29724 pos = pos || node.position();
29725 var cyStyle = node.cy().style();
29726 var pieSize = node.pstyle('pie-size');
29727 var x = pos.x;
29728 var y = pos.y;
29729 var nodeW = node.width();
29730 var nodeH = node.height();
29731 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
29732
29733 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
29734
29735 var usePaths = this.usePaths();
29736
29737 if (usePaths) {
29738 x = 0;
29739 y = 0;
29740 }
29741
29742 if (pieSize.units === '%') {
29743 radius = radius * pieSize.pfValue;
29744 } else if (pieSize.pfValue !== undefined) {
29745 radius = pieSize.pfValue / 2;
29746 }
29747
29748 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
29749 // 1..N
29750 var size = node.pstyle('pie-' + i + '-background-size').value;
29751 var color = node.pstyle('pie-' + i + '-background-color').value;
29752 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
29753 var percent = size / 100; // map integer range [0, 100] to [0, 1]
29754 // percent can't push beyond 1
29755
29756 if (percent + lastPercent > 1) {
29757 percent = 1 - lastPercent;
29758 }
29759
29760 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
29761
29762 var angleDelta = 2 * Math.PI * percent;
29763 var angleEnd = angleStart + angleDelta; // ignore if
29764 // - zero size
29765 // - we're already beyond the full circle
29766 // - adding the current slice would go beyond the full circle
29767
29768 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
29769 continue;
29770 }
29771
29772 context.beginPath();
29773 context.moveTo(x, y);
29774 context.arc(x, y, radius, angleStart, angleEnd);
29775 context.closePath();
29776 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29777 context.fill();
29778 lastPercent += percent;
29779 }
29780};
29781
29782var CRp$6 = {};
29783var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
29784
29785CRp$6.getPixelRatio = function () {
29786 var context = this.data.contexts[0];
29787
29788 if (this.forcedPixelRatio != null) {
29789 return this.forcedPixelRatio;
29790 }
29791
29792 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
29793 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
29794};
29795
29796CRp$6.paintCache = function (context) {
29797 var caches = this.paintCaches = this.paintCaches || [];
29798 var needToCreateCache = true;
29799 var cache;
29800
29801 for (var i = 0; i < caches.length; i++) {
29802 cache = caches[i];
29803
29804 if (cache.context === context) {
29805 needToCreateCache = false;
29806 break;
29807 }
29808 }
29809
29810 if (needToCreateCache) {
29811 cache = {
29812 context: context
29813 };
29814 caches.push(cache);
29815 }
29816
29817 return cache;
29818};
29819
29820CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
29821 var gradientStyle;
29822 var usePaths = this.usePaths();
29823 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
29824 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
29825
29826 if (fill === 'radial-gradient') {
29827 if (ele.isEdge()) {
29828 var start = ele.sourceEndpoint(),
29829 end = ele.targetEndpoint(),
29830 mid = ele.midpoint();
29831 var d1 = dist(start, mid);
29832 var d2 = dist(end, mid);
29833 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
29834 } else {
29835 var pos = usePaths ? {
29836 x: 0,
29837 y: 0
29838 } : ele.position(),
29839 width = ele.paddedWidth(),
29840 height = ele.paddedHeight();
29841 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
29842 }
29843 } else {
29844 if (ele.isEdge()) {
29845 var _start = ele.sourceEndpoint(),
29846 _end = ele.targetEndpoint();
29847
29848 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
29849 } else {
29850 var _pos = usePaths ? {
29851 x: 0,
29852 y: 0
29853 } : ele.position(),
29854 _width = ele.paddedWidth(),
29855 _height = ele.paddedHeight(),
29856 halfWidth = _width / 2,
29857 halfHeight = _height / 2;
29858
29859 var direction = ele.pstyle('background-gradient-direction').value;
29860
29861 switch (direction) {
29862 case 'to-bottom':
29863 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
29864 break;
29865
29866 case 'to-top':
29867 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
29868 break;
29869
29870 case 'to-left':
29871 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
29872 break;
29873
29874 case 'to-right':
29875 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
29876 break;
29877
29878 case 'to-bottom-right':
29879 case 'to-right-bottom':
29880 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
29881 break;
29882
29883 case 'to-top-right':
29884 case 'to-right-top':
29885 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
29886 break;
29887
29888 case 'to-bottom-left':
29889 case 'to-left-bottom':
29890 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
29891 break;
29892
29893 case 'to-top-left':
29894 case 'to-left-top':
29895 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
29896 break;
29897 }
29898 }
29899 }
29900
29901 if (!gradientStyle) return null; // invalid gradient style
29902
29903 var hasPositions = positions.length === colors.length;
29904 var length = colors.length;
29905
29906 for (var i = 0; i < length; i++) {
29907 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
29908 }
29909
29910 return gradientStyle;
29911};
29912
29913CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
29914 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
29915 if (!gradientStyle) return null; // error
29916
29917 context.fillStyle = gradientStyle;
29918};
29919
29920CRp$6.colorFillStyle = function (context, r, g, b, a) {
29921 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
29922 // var cache = this.paintCache(context);
29923 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29924 // if( cache.fillStyle !== fillStyle ){
29925 // context.fillStyle = cache.fillStyle = fillStyle;
29926 // }
29927};
29928
29929CRp$6.eleFillStyle = function (context, ele, opacity) {
29930 var backgroundFill = ele.pstyle('background-fill').value;
29931
29932 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
29933 this.gradientFillStyle(context, ele, backgroundFill, opacity);
29934 } else {
29935 var backgroundColor = ele.pstyle('background-color').value;
29936 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
29937 }
29938};
29939
29940CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
29941 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
29942 if (!gradientStyle) return null; // error
29943
29944 context.strokeStyle = gradientStyle;
29945};
29946
29947CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
29948 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
29949 // var cache = this.paintCache(context);
29950 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29951 // if( cache.strokeStyle !== strokeStyle ){
29952 // context.strokeStyle = cache.strokeStyle = strokeStyle;
29953 // }
29954};
29955
29956CRp$6.eleStrokeStyle = function (context, ele, opacity) {
29957 var lineFill = ele.pstyle('line-fill').value;
29958
29959 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
29960 this.gradientStrokeStyle(context, ele, lineFill, opacity);
29961 } else {
29962 var lineColor = ele.pstyle('line-color').value;
29963 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
29964 }
29965}; // Resize canvas
29966
29967
29968CRp$6.matchCanvasSize = function (container) {
29969 var r = this;
29970 var data = r.data;
29971 var bb = r.findContainerClientCoords();
29972 var width = bb[2];
29973 var height = bb[3];
29974 var pixelRatio = r.getPixelRatio();
29975 var mbPxRatio = r.motionBlurPxRatio;
29976
29977 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
29978 pixelRatio = mbPxRatio;
29979 }
29980
29981 var canvasWidth = width * pixelRatio;
29982 var canvasHeight = height * pixelRatio;
29983 var canvas;
29984
29985 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
29986 return; // save cycles if same
29987 }
29988
29989 r.fontCaches = null; // resizing resets the style
29990
29991 var canvasContainer = data.canvasContainer;
29992 canvasContainer.style.width = width + 'px';
29993 canvasContainer.style.height = height + 'px';
29994
29995 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
29996 canvas = data.canvases[i];
29997 canvas.width = canvasWidth;
29998 canvas.height = canvasHeight;
29999 canvas.style.width = width + 'px';
30000 canvas.style.height = height + 'px';
30001 }
30002
30003 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30004 canvas = data.bufferCanvases[i];
30005 canvas.width = canvasWidth;
30006 canvas.height = canvasHeight;
30007 canvas.style.width = width + 'px';
30008 canvas.style.height = height + 'px';
30009 }
30010
30011 r.textureMult = 1;
30012
30013 if (pixelRatio <= 1) {
30014 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30015 r.textureMult = 2;
30016 canvas.width = canvasWidth * r.textureMult;
30017 canvas.height = canvasHeight * r.textureMult;
30018 }
30019
30020 r.canvasWidth = canvasWidth;
30021 r.canvasHeight = canvasHeight;
30022};
30023
30024CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30025 this.render({
30026 forcedContext: cxt,
30027 forcedZoom: zoom,
30028 forcedPan: pan,
30029 drawAllLayers: true,
30030 forcedPxRatio: pxRatio
30031 });
30032};
30033
30034CRp$6.render = function (options) {
30035 options = options || staticEmptyObject();
30036 var forcedContext = options.forcedContext;
30037 var drawAllLayers = options.drawAllLayers;
30038 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30039 var forcedZoom = options.forcedZoom;
30040 var forcedPan = options.forcedPan;
30041 var r = this;
30042 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30043 var cy = r.cy;
30044 var data = r.data;
30045 var needDraw = data.canvasNeedsRedraw;
30046 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30047 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30048 var mbPxRatio = r.motionBlurPxRatio;
30049 var hasCompoundNodes = cy.hasCompoundNodes();
30050 var inNodeDragGesture = r.hoverData.draggingEles;
30051 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30052 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30053 var motionBlurFadeEffect = motionBlur;
30054
30055 if (!forcedContext) {
30056 if (r.prevPxRatio !== pixelRatio) {
30057 r.invalidateContainerClientCoordsCache();
30058 r.matchCanvasSize(r.container);
30059 r.redrawHint('eles', true);
30060 r.redrawHint('drag', true);
30061 }
30062
30063 r.prevPxRatio = pixelRatio;
30064 }
30065
30066 if (!forcedContext && r.motionBlurTimeout) {
30067 clearTimeout(r.motionBlurTimeout);
30068 }
30069
30070 if (motionBlur) {
30071 if (r.mbFrames == null) {
30072 r.mbFrames = 0;
30073 }
30074
30075 r.mbFrames++;
30076
30077 if (r.mbFrames < 3) {
30078 // need several frames before even high quality motionblur
30079 motionBlurFadeEffect = false;
30080 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30081
30082
30083 if (r.mbFrames > r.minMbLowQualFrames) {
30084 //r.fullQualityMb = false;
30085 r.motionBlurPxRatio = r.mbPxRBlurry;
30086 }
30087 }
30088
30089 if (r.clearingMotionBlur) {
30090 r.motionBlurPxRatio = 1;
30091 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30092 // because a rogue async texture frame would clear needDraw
30093
30094
30095 if (r.textureDrawLastFrame && !textureDraw) {
30096 needDraw[r.NODE] = true;
30097 needDraw[r.SELECT_BOX] = true;
30098 }
30099
30100 var style = cy.style();
30101 var zoom = cy.zoom();
30102 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30103 var pan = cy.pan();
30104 var effectivePan = {
30105 x: pan.x,
30106 y: pan.y
30107 };
30108 var vp = {
30109 zoom: zoom,
30110 pan: {
30111 x: pan.x,
30112 y: pan.y
30113 }
30114 };
30115 var prevVp = r.prevViewport;
30116 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)
30117
30118 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30119 r.motionBlurPxRatio = 1;
30120 }
30121
30122 if (forcedPan) {
30123 effectivePan = forcedPan;
30124 } // apply pixel ratio
30125
30126
30127 effectiveZoom *= pixelRatio;
30128 effectivePan.x *= pixelRatio;
30129 effectivePan.y *= pixelRatio;
30130 var eles = r.getCachedZSortedEles();
30131
30132 function mbclear(context, x, y, w, h) {
30133 var gco = context.globalCompositeOperation;
30134 context.globalCompositeOperation = 'destination-out';
30135 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30136 context.fillRect(x, y, w, h);
30137 context.globalCompositeOperation = gco;
30138 }
30139
30140 function setContextTransform(context, clear) {
30141 var ePan, eZoom, w, h;
30142
30143 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30144 ePan = {
30145 x: pan.x * mbPxRatio,
30146 y: pan.y * mbPxRatio
30147 };
30148 eZoom = zoom * mbPxRatio;
30149 w = r.canvasWidth * mbPxRatio;
30150 h = r.canvasHeight * mbPxRatio;
30151 } else {
30152 ePan = effectivePan;
30153 eZoom = effectiveZoom;
30154 w = r.canvasWidth;
30155 h = r.canvasHeight;
30156 }
30157
30158 context.setTransform(1, 0, 0, 1, 0, 0);
30159
30160 if (clear === 'motionBlur') {
30161 mbclear(context, 0, 0, w, h);
30162 } else if (!forcedContext && (clear === undefined || clear)) {
30163 context.clearRect(0, 0, w, h);
30164 }
30165
30166 if (!drawAllLayers) {
30167 context.translate(ePan.x, ePan.y);
30168 context.scale(eZoom, eZoom);
30169 }
30170
30171 if (forcedPan) {
30172 context.translate(forcedPan.x, forcedPan.y);
30173 }
30174
30175 if (forcedZoom) {
30176 context.scale(forcedZoom, forcedZoom);
30177 }
30178 }
30179
30180 if (!textureDraw) {
30181 r.textureDrawLastFrame = false;
30182 }
30183
30184 if (textureDraw) {
30185 r.textureDrawLastFrame = true;
30186
30187 if (!r.textureCache) {
30188 r.textureCache = {};
30189 r.textureCache.bb = cy.mutableElements().boundingBox();
30190 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30191 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30192 cxt.setTransform(1, 0, 0, 1, 0, 0);
30193 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30194 r.render({
30195 forcedContext: cxt,
30196 drawOnlyNodeLayer: true,
30197 forcedPxRatio: pixelRatio * r.textureMult
30198 });
30199 var vp = r.textureCache.viewport = {
30200 zoom: cy.zoom(),
30201 pan: cy.pan(),
30202 width: r.canvasWidth,
30203 height: r.canvasHeight
30204 };
30205 vp.mpan = {
30206 x: (0 - vp.pan.x) / vp.zoom,
30207 y: (0 - vp.pan.y) / vp.zoom
30208 };
30209 }
30210
30211 needDraw[r.DRAG] = false;
30212 needDraw[r.NODE] = false;
30213 var context = data.contexts[r.NODE];
30214 var texture = r.textureCache.texture;
30215 var vp = r.textureCache.viewport;
30216 context.setTransform(1, 0, 0, 1, 0, 0);
30217
30218 if (motionBlur) {
30219 mbclear(context, 0, 0, vp.width, vp.height);
30220 } else {
30221 context.clearRect(0, 0, vp.width, vp.height);
30222 }
30223
30224 var outsideBgColor = style.core('outside-texture-bg-color').value;
30225 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30226 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30227 context.fillRect(0, 0, vp.width, vp.height);
30228 var zoom = cy.zoom();
30229 setContextTransform(context, false);
30230 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30231 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30232 } else if (r.textureOnViewport && !forcedContext) {
30233 // clear the cache since we don't need it
30234 r.textureCache = null;
30235 }
30236
30237 var extent = cy.extent();
30238 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30239 var hideEdges = r.hideEdgesOnViewport && vpManip;
30240 var needMbClear = [];
30241 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30242
30243 if (needMbClear[r.NODE]) {
30244 r.clearedForMotionBlur[r.NODE] = true;
30245 }
30246
30247 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30248
30249 if (needMbClear[r.DRAG]) {
30250 r.clearedForMotionBlur[r.DRAG] = true;
30251 }
30252
30253 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30254 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30255 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30256 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30257 setContextTransform(context, clear);
30258
30259 if (hideEdges) {
30260 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30261 } else {
30262 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30263 }
30264
30265 if (r.debug) {
30266 r.drawDebugPoints(context, eles.nondrag);
30267 }
30268
30269 if (!drawAllLayers && !motionBlur) {
30270 needDraw[r.NODE] = false;
30271 }
30272 }
30273
30274 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30275 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30276 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30277 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30278
30279 if (hideEdges) {
30280 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30281 } else {
30282 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30283 }
30284
30285 if (r.debug) {
30286 r.drawDebugPoints(context, eles.drag);
30287 }
30288
30289 if (!drawAllLayers && !motionBlur) {
30290 needDraw[r.DRAG] = false;
30291 }
30292 }
30293
30294 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30295 var context = forcedContext || data.contexts[r.SELECT_BOX];
30296 setContextTransform(context);
30297
30298 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30299 var zoom = r.cy.zoom();
30300 var borderWidth = style.core('selection-box-border-width').value / zoom;
30301 context.lineWidth = borderWidth;
30302 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 + ')';
30303 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30304
30305 if (borderWidth > 0) {
30306 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 + ')';
30307 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30308 }
30309 }
30310
30311 if (data.bgActivePosistion && !r.hoverData.selecting) {
30312 var zoom = r.cy.zoom();
30313 var pos = data.bgActivePosistion;
30314 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 + ')';
30315 context.beginPath();
30316 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30317 context.fill();
30318 }
30319
30320 var timeToRender = r.lastRedrawTime;
30321
30322 if (r.showFps && timeToRender) {
30323 timeToRender = Math.round(timeToRender);
30324 var fps = Math.round(1000 / timeToRender);
30325 context.setTransform(1, 0, 0, 1, 0, 0);
30326 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30327 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30328 context.lineWidth = 1;
30329 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30330 var maxFps = 60;
30331 context.strokeRect(0, 30, 250, 20);
30332 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30333 }
30334
30335 if (!drawAllLayers) {
30336 needDraw[r.SELECT_BOX] = false;
30337 }
30338 } // motionblur: blit rendered blurry frames
30339
30340
30341 if (motionBlur && mbPxRatio !== 1) {
30342 var cxtNode = data.contexts[r.NODE];
30343 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30344 var cxtDrag = data.contexts[r.DRAG];
30345 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30346
30347 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30348 cxt.setTransform(1, 0, 0, 1, 0, 0);
30349
30350 if (needClear || !motionBlurFadeEffect) {
30351 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30352 } else {
30353 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30354 }
30355
30356 var pxr = mbPxRatio;
30357 cxt.drawImage(txt, // img
30358 0, 0, // sx, sy
30359 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30360 0, 0, // x, y
30361 r.canvasWidth, r.canvasHeight // w, h
30362 );
30363 };
30364
30365 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30366 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30367 needDraw[r.NODE] = false;
30368 }
30369
30370 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30371 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30372 needDraw[r.DRAG] = false;
30373 }
30374 }
30375
30376 r.prevViewport = vp;
30377
30378 if (r.clearingMotionBlur) {
30379 r.clearingMotionBlur = false;
30380 r.motionBlurCleared = true;
30381 r.motionBlur = true;
30382 }
30383
30384 if (motionBlur) {
30385 r.motionBlurTimeout = setTimeout(function () {
30386 r.motionBlurTimeout = null;
30387 r.clearedForMotionBlur[r.NODE] = false;
30388 r.clearedForMotionBlur[r.DRAG] = false;
30389 r.motionBlur = false;
30390 r.clearingMotionBlur = !textureDraw;
30391 r.mbFrames = 0;
30392 needDraw[r.NODE] = true;
30393 needDraw[r.DRAG] = true;
30394 r.redraw();
30395 }, motionBlurDelay);
30396 }
30397
30398 if (!forcedContext) {
30399 cy.emit('render');
30400 }
30401};
30402
30403var CRp$7 = {}; // @O Polygon drawing
30404
30405CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
30406 var halfW = width / 2;
30407 var halfH = height / 2;
30408
30409 if (context.beginPath) {
30410 context.beginPath();
30411 }
30412
30413 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30414
30415 for (var i = 1; i < points.length / 2; i++) {
30416 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30417 }
30418
30419 context.closePath();
30420};
30421
30422CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30423 var halfW = width / 2;
30424 var halfH = height / 2;
30425 var cornerRadius = getRoundPolygonRadius(width, height);
30426
30427 if (context.beginPath) {
30428 context.beginPath();
30429 }
30430
30431 for (var _i = 0; _i < points.length / 4; _i++) {
30432 var sourceUv = void 0,
30433 destUv = void 0;
30434
30435 if (_i === 0) {
30436 sourceUv = points.length - 2;
30437 } else {
30438 sourceUv = _i * 4 - 2;
30439 }
30440
30441 destUv = _i * 4 + 2;
30442 var px = x + halfW * points[_i * 4];
30443 var py = y + halfH * points[_i * 4 + 1];
30444 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30445 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30446 var cp0x = px - offset * points[sourceUv];
30447 var cp0y = py - offset * points[sourceUv + 1];
30448 var cp1x = px + offset * points[destUv];
30449 var cp1y = py + offset * points[destUv + 1];
30450
30451 if (_i === 0) {
30452 context.moveTo(cp0x, cp0y);
30453 } else {
30454 context.lineTo(cp0x, cp0y);
30455 }
30456
30457 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30458 }
30459
30460 context.closePath();
30461}; // Round rectangle drawing
30462
30463
30464CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
30465 var halfWidth = width / 2;
30466 var halfHeight = height / 2;
30467 var cornerRadius = getRoundRectangleRadius(width, height);
30468
30469 if (context.beginPath) {
30470 context.beginPath();
30471 } // Start at top middle
30472
30473
30474 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30475
30476 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30477
30478 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30479
30480 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30481
30482 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30483
30484 context.lineTo(x, y - halfHeight);
30485 context.closePath();
30486};
30487
30488CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30489 var halfWidth = width / 2;
30490 var halfHeight = height / 2;
30491 var cornerRadius = getRoundRectangleRadius(width, height);
30492
30493 if (context.beginPath) {
30494 context.beginPath();
30495 } // Start at top middle
30496
30497
30498 context.moveTo(x, y - halfHeight);
30499 context.lineTo(x + halfWidth, y - halfHeight);
30500 context.lineTo(x + halfWidth, y);
30501 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30502 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30503 context.lineTo(x - halfWidth, y - halfHeight);
30504 context.lineTo(x, y - halfHeight);
30505 context.closePath();
30506};
30507
30508CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
30509 var halfWidth = width / 2;
30510 var halfHeight = height / 2;
30511 var cornerLength = getCutRectangleCornerLength();
30512
30513 if (context.beginPath) {
30514 context.beginPath();
30515 }
30516
30517 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30518 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30519 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30520 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30521 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30522 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30523 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30524 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30525 context.closePath();
30526};
30527
30528CRp$7.drawBarrelPath = function (context, x, y, width, height) {
30529 var halfWidth = width / 2;
30530 var halfHeight = height / 2;
30531 var xBegin = x - halfWidth;
30532 var xEnd = x + halfWidth;
30533 var yBegin = y - halfHeight;
30534 var yEnd = y + halfHeight;
30535 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30536 var wOffset = barrelCurveConstants.widthOffset;
30537 var hOffset = barrelCurveConstants.heightOffset;
30538 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30539
30540 if (context.beginPath) {
30541 context.beginPath();
30542 }
30543
30544 context.moveTo(xBegin, yBegin + hOffset);
30545 context.lineTo(xBegin, yEnd - hOffset);
30546 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30547 context.lineTo(xEnd - wOffset, yEnd);
30548 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30549 context.lineTo(xEnd, yBegin + hOffset);
30550 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30551 context.lineTo(xBegin + wOffset, yBegin);
30552 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30553 context.closePath();
30554};
30555
30556var sin0 = Math.sin(0);
30557var cos0 = Math.cos(0);
30558var sin = {};
30559var cos = {};
30560var ellipseStepSize = Math.PI / 40;
30561
30562for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30563 sin[i] = Math.sin(i);
30564 cos[i] = Math.cos(i);
30565}
30566
30567CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
30568 if (context.beginPath) {
30569 context.beginPath();
30570 }
30571
30572 if (context.ellipse) {
30573 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30574 } else {
30575 var xPos, yPos;
30576 var rw = width / 2;
30577 var rh = height / 2;
30578
30579 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30580 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30581 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30582
30583 if (i === 0) {
30584 context.moveTo(xPos, yPos);
30585 } else {
30586 context.lineTo(xPos, yPos);
30587 }
30588 }
30589 }
30590
30591 context.closePath();
30592};
30593
30594/* global atob, ArrayBuffer, Uint8Array, Blob */
30595var CRp$8 = {};
30596
30597CRp$8.createBuffer = function (w, h) {
30598 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30599
30600 buffer.width = w;
30601 buffer.height = h;
30602 return [buffer, buffer.getContext('2d')];
30603};
30604
30605CRp$8.bufferCanvasImage = function (options) {
30606 var cy = this.cy;
30607 var eles = cy.mutableElements();
30608 var bb = eles.boundingBox();
30609 var ctrRect = this.findContainerClientCoords();
30610 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
30611 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
30612 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
30613 var pxRatio = this.getPixelRatio();
30614 var scale = 1;
30615
30616 if (options.scale !== undefined) {
30617 width *= options.scale;
30618 height *= options.scale;
30619 scale = options.scale;
30620 } else if (specdMaxDims) {
30621 var maxScaleW = Infinity;
30622 var maxScaleH = Infinity;
30623
30624 if (number(options.maxWidth)) {
30625 maxScaleW = scale * options.maxWidth / width;
30626 }
30627
30628 if (number(options.maxHeight)) {
30629 maxScaleH = scale * options.maxHeight / height;
30630 }
30631
30632 scale = Math.min(maxScaleW, maxScaleH);
30633 width *= scale;
30634 height *= scale;
30635 }
30636
30637 if (!specdMaxDims) {
30638 width *= pxRatio;
30639 height *= pxRatio;
30640 scale *= pxRatio;
30641 }
30642
30643 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
30644
30645 buffCanvas.width = width;
30646 buffCanvas.height = height;
30647 buffCanvas.style.width = width + 'px';
30648 buffCanvas.style.height = height + 'px';
30649 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
30650
30651 if (width > 0 && height > 0) {
30652 buffCxt.clearRect(0, 0, width, height);
30653 buffCxt.globalCompositeOperation = 'source-over';
30654 var zsortedEles = this.getCachedZSortedEles();
30655
30656 if (options.full) {
30657 // draw the full bounds of the graph
30658 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
30659 buffCxt.scale(scale, scale);
30660 this.drawElements(buffCxt, zsortedEles);
30661 buffCxt.scale(1 / scale, 1 / scale);
30662 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
30663 } else {
30664 // draw the current view
30665 var pan = cy.pan();
30666 var translation = {
30667 x: pan.x * scale,
30668 y: pan.y * scale
30669 };
30670 scale *= cy.zoom();
30671 buffCxt.translate(translation.x, translation.y);
30672 buffCxt.scale(scale, scale);
30673 this.drawElements(buffCxt, zsortedEles);
30674 buffCxt.scale(1 / scale, 1 / scale);
30675 buffCxt.translate(-translation.x, -translation.y);
30676 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
30677
30678
30679 if (options.bg) {
30680 buffCxt.globalCompositeOperation = 'destination-over';
30681 buffCxt.fillStyle = options.bg;
30682 buffCxt.rect(0, 0, width, height);
30683 buffCxt.fill();
30684 }
30685 }
30686
30687 return buffCanvas;
30688};
30689
30690function b64ToBlob(b64, mimeType) {
30691 var bytes = atob(b64);
30692 var buff = new ArrayBuffer(bytes.length);
30693 var buffUint8 = new Uint8Array(buff);
30694
30695 for (var i = 0; i < bytes.length; i++) {
30696 buffUint8[i] = bytes.charCodeAt(i);
30697 }
30698
30699 return new Blob([buff], {
30700 type: mimeType
30701 });
30702}
30703
30704function b64UriToB64(b64uri) {
30705 var i = b64uri.indexOf(',');
30706 return b64uri.substr(i + 1);
30707}
30708
30709function output(options, canvas, mimeType) {
30710 var getB64Uri = function getB64Uri() {
30711 return canvas.toDataURL(mimeType, options.quality);
30712 };
30713
30714 switch (options.output) {
30715 case 'blob-promise':
30716 return new Promise$1(function (resolve, reject) {
30717 try {
30718 canvas.toBlob(function (blob) {
30719 if (blob != null) {
30720 resolve(blob);
30721 } else {
30722 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
30723 }
30724 }, mimeType, options.quality);
30725 } catch (err) {
30726 reject(err);
30727 }
30728 });
30729
30730 case 'blob':
30731 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
30732
30733 case 'base64':
30734 return b64UriToB64(getB64Uri());
30735
30736 case 'base64uri':
30737 default:
30738 return getB64Uri();
30739 }
30740}
30741
30742CRp$8.png = function (options) {
30743 return output(options, this.bufferCanvasImage(options), 'image/png');
30744};
30745
30746CRp$8.jpg = function (options) {
30747 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
30748};
30749
30750var CRp$9 = {};
30751
30752CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
30753 switch (name) {
30754 case 'ellipse':
30755 return this.drawEllipsePath(context, centerX, centerY, width, height);
30756
30757 case 'polygon':
30758 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
30759
30760 case 'round-polygon':
30761 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
30762
30763 case 'roundrectangle':
30764 case 'round-rectangle':
30765 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
30766
30767 case 'cutrectangle':
30768 case 'cut-rectangle':
30769 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
30770
30771 case 'bottomroundrectangle':
30772 case 'bottom-round-rectangle':
30773 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
30774
30775 case 'barrel':
30776 return this.drawBarrelPath(context, centerX, centerY, width, height);
30777 }
30778};
30779
30780var CR = CanvasRenderer;
30781var CRp$a = CanvasRenderer.prototype;
30782CRp$a.CANVAS_LAYERS = 3; //
30783
30784CRp$a.SELECT_BOX = 0;
30785CRp$a.DRAG = 1;
30786CRp$a.NODE = 2;
30787CRp$a.BUFFER_COUNT = 3; //
30788
30789CRp$a.TEXTURE_BUFFER = 0;
30790CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
30791CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
30792
30793function CanvasRenderer(options) {
30794 var r = this;
30795 r.data = {
30796 canvases: new Array(CRp$a.CANVAS_LAYERS),
30797 contexts: new Array(CRp$a.CANVAS_LAYERS),
30798 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
30799 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
30800 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
30801 };
30802 var tapHlOffAttr = '-webkit-tap-highlight-color';
30803 var tapHlOffStyle = 'rgba(0,0,0,0)';
30804 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
30805
30806 var containerStyle = r.data.canvasContainer.style;
30807 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
30808 containerStyle.position = 'relative';
30809 containerStyle.zIndex = '0';
30810 containerStyle.overflow = 'hidden';
30811 var container = options.cy.container();
30812 container.appendChild(r.data.canvasContainer);
30813 container.style[tapHlOffAttr] = tapHlOffStyle;
30814 var styleMap = {
30815 '-webkit-user-select': 'none',
30816 '-moz-user-select': '-moz-none',
30817 'user-select': 'none',
30818 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
30819 'outline-style': 'none'
30820 };
30821
30822 if (ms()) {
30823 styleMap['-ms-touch-action'] = 'none';
30824 styleMap['touch-action'] = 'none';
30825 }
30826
30827 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
30828 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30829
30830 r.data.contexts[i] = canvas.getContext('2d');
30831 Object.keys(styleMap).forEach(function (k) {
30832 canvas.style[k] = styleMap[k];
30833 });
30834 canvas.style.position = 'absolute';
30835 canvas.setAttribute('data-id', 'layer' + i);
30836 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
30837 r.data.canvasContainer.appendChild(canvas);
30838 r.data.canvasNeedsRedraw[i] = false;
30839 }
30840
30841 r.data.topCanvas = r.data.canvases[0];
30842 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
30843 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
30844 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
30845
30846 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
30847 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30848
30849 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
30850 r.data.bufferCanvases[i].style.position = 'absolute';
30851 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
30852 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
30853 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
30854 }
30855
30856 r.pathsEnabled = true;
30857 var emptyBb = makeBoundingBox();
30858
30859 var getBoxCenter = function getBoxCenter(bb) {
30860 return {
30861 x: (bb.x1 + bb.x2) / 2,
30862 y: (bb.y1 + bb.y2) / 2
30863 };
30864 };
30865
30866 var getCenterOffset = function getCenterOffset(bb) {
30867 return {
30868 x: -bb.w / 2,
30869 y: -bb.h / 2
30870 };
30871 };
30872
30873 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
30874 var _p = ele[0]._private;
30875 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
30876 return !same;
30877 };
30878
30879 var getStyleKey = function getStyleKey(ele) {
30880 return ele[0]._private.nodeKey;
30881 };
30882
30883 var getLabelKey = function getLabelKey(ele) {
30884 return ele[0]._private.labelStyleKey;
30885 };
30886
30887 var getSourceLabelKey = function getSourceLabelKey(ele) {
30888 return ele[0]._private.sourceLabelStyleKey;
30889 };
30890
30891 var getTargetLabelKey = function getTargetLabelKey(ele) {
30892 return ele[0]._private.targetLabelStyleKey;
30893 };
30894
30895 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
30896 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
30897 };
30898
30899 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30900 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
30901 };
30902
30903 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30904 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
30905 };
30906
30907 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30908 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
30909 };
30910
30911 var getElementBox = function getElementBox(ele) {
30912 ele.boundingBox();
30913 return ele[0]._private.bodyBounds;
30914 };
30915
30916 var getLabelBox = function getLabelBox(ele) {
30917 ele.boundingBox();
30918 return ele[0]._private.labelBounds.main || emptyBb;
30919 };
30920
30921 var getSourceLabelBox = function getSourceLabelBox(ele) {
30922 ele.boundingBox();
30923 return ele[0]._private.labelBounds.source || emptyBb;
30924 };
30925
30926 var getTargetLabelBox = function getTargetLabelBox(ele) {
30927 ele.boundingBox();
30928 return ele[0]._private.labelBounds.target || emptyBb;
30929 };
30930
30931 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
30932 return scaledLabelShown;
30933 };
30934
30935 var getElementRotationPoint = function getElementRotationPoint(ele) {
30936 return getBoxCenter(getElementBox(ele));
30937 };
30938
30939 var addTextMargin = function addTextMargin(prefix, pt, ele) {
30940 var pre = prefix ? prefix + '-' : '';
30941 return {
30942 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
30943 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
30944 };
30945 };
30946
30947 var getRsPt = function getRsPt(ele, x, y) {
30948 var rs = ele[0]._private.rscratch;
30949 return {
30950 x: rs[x],
30951 y: rs[y]
30952 };
30953 };
30954
30955 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
30956 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
30957 };
30958
30959 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
30960 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
30961 };
30962
30963 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
30964 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
30965 };
30966
30967 var getElementRotationOffset = function getElementRotationOffset(ele) {
30968 return getCenterOffset(getElementBox(ele));
30969 };
30970
30971 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
30972 return getCenterOffset(getSourceLabelBox(ele));
30973 };
30974
30975 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
30976 return getCenterOffset(getTargetLabelBox(ele));
30977 };
30978
30979 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
30980 var bb = getLabelBox(ele);
30981 var p = getCenterOffset(getLabelBox(ele));
30982
30983 if (ele.isNode()) {
30984 switch (ele.pstyle('text-halign').value) {
30985 case 'left':
30986 p.x = -bb.w;
30987 break;
30988
30989 case 'right':
30990 p.x = 0;
30991 break;
30992 }
30993
30994 switch (ele.pstyle('text-valign').value) {
30995 case 'top':
30996 p.y = -bb.h;
30997 break;
30998
30999 case 'bottom':
31000 p.y = 0;
31001 break;
31002 }
31003 }
31004
31005 return p;
31006 };
31007
31008 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31009 getKey: getStyleKey,
31010 doesEleInvalidateKey: backgroundTimestampHasChanged,
31011 drawElement: drawElement,
31012 getBoundingBox: getElementBox,
31013 getRotationPoint: getElementRotationPoint,
31014 getRotationOffset: getElementRotationOffset,
31015 allowEdgeTxrCaching: false,
31016 allowParentTxrCaching: false
31017 });
31018 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31019 getKey: getLabelKey,
31020 drawElement: drawLabel,
31021 getBoundingBox: getLabelBox,
31022 getRotationPoint: getLabelRotationPoint,
31023 getRotationOffset: getLabelRotationOffset,
31024 isVisible: isLabelVisibleAtScale
31025 });
31026 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31027 getKey: getSourceLabelKey,
31028 drawElement: drawSourceLabel,
31029 getBoundingBox: getSourceLabelBox,
31030 getRotationPoint: getSourceLabelRotationPoint,
31031 getRotationOffset: getSourceLabelRotationOffset,
31032 isVisible: isLabelVisibleAtScale
31033 });
31034 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31035 getKey: getTargetLabelKey,
31036 drawElement: drawTargetLabel,
31037 getBoundingBox: getTargetLabelBox,
31038 getRotationPoint: getTargetLabelRotationPoint,
31039 getRotationOffset: getTargetLabelRotationOffset,
31040 isVisible: isLabelVisibleAtScale
31041 });
31042 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31043 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31044 // each cache should check for sub-key diff to see that the update affects that cache particularly
31045 eleTxrCache.invalidateElements(eles);
31046 lblTxrCache.invalidateElements(eles);
31047 slbTxrCache.invalidateElements(eles);
31048 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31049
31050 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31051
31052 for (var _i = 0; _i < eles.length; _i++) {
31053 var _p = eles[_i]._private;
31054 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31055 }
31056 });
31057
31058 var refineInLayers = function refineInLayers(reqs) {
31059 for (var i = 0; i < reqs.length; i++) {
31060 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31061 }
31062 };
31063
31064 eleTxrCache.onDequeue(refineInLayers);
31065 lblTxrCache.onDequeue(refineInLayers);
31066 slbTxrCache.onDequeue(refineInLayers);
31067 tlbTxrCache.onDequeue(refineInLayers);
31068}
31069
31070CRp$a.redrawHint = function (group, bool) {
31071 var r = this;
31072
31073 switch (group) {
31074 case 'eles':
31075 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31076 break;
31077
31078 case 'drag':
31079 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31080 break;
31081
31082 case 'select':
31083 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31084 break;
31085 }
31086}; // whether to use Path2D caching for drawing
31087
31088
31089var pathsImpld = typeof Path2D !== 'undefined';
31090
31091CRp$a.path2dEnabled = function (on) {
31092 if (on === undefined) {
31093 return this.pathsEnabled;
31094 }
31095
31096 this.pathsEnabled = on ? true : false;
31097};
31098
31099CRp$a.usePaths = function () {
31100 return pathsImpld && this.pathsEnabled;
31101};
31102
31103CRp$a.setImgSmoothing = function (context, bool) {
31104 if (context.imageSmoothingEnabled != null) {
31105 context.imageSmoothingEnabled = bool;
31106 } else {
31107 context.webkitImageSmoothingEnabled = bool;
31108 context.mozImageSmoothingEnabled = bool;
31109 context.msImageSmoothingEnabled = bool;
31110 }
31111};
31112
31113CRp$a.getImgSmoothing = function (context) {
31114 if (context.imageSmoothingEnabled != null) {
31115 return context.imageSmoothingEnabled;
31116 } else {
31117 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31118 }
31119};
31120
31121CRp$a.makeOffscreenCanvas = function (width, height) {
31122 var canvas;
31123
31124 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
31125 canvas = new OffscreenCanvas(width, height);
31126 } else {
31127 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31128
31129 canvas.width = width;
31130 canvas.height = height;
31131 }
31132
31133 return canvas;
31134};
31135
31136[CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
31137 extend(CRp$a, props);
31138});
31139
31140var renderer = [{
31141 name: 'null',
31142 impl: NullRenderer
31143}, {
31144 name: 'base',
31145 impl: BR
31146}, {
31147 name: 'canvas',
31148 impl: CR
31149}];
31150
31151var incExts = [{
31152 type: 'layout',
31153 extensions: layout
31154}, {
31155 type: 'renderer',
31156 extensions: renderer
31157}];
31158
31159var extensions = {}; // registered modules for extensions, indexed by name
31160
31161var modules = {};
31162
31163function setExtension(type, name, registrant) {
31164 var ext = registrant;
31165
31166 var overrideErr = function overrideErr(field) {
31167 error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31168 };
31169
31170 if (type === 'core') {
31171 if (Core.prototype[name]) {
31172 return overrideErr(name);
31173 } else {
31174 Core.prototype[name] = registrant;
31175 }
31176 } else if (type === 'collection') {
31177 if (Collection.prototype[name]) {
31178 return overrideErr(name);
31179 } else {
31180 Collection.prototype[name] = registrant;
31181 }
31182 } else if (type === 'layout') {
31183 // fill in missing layout functions in the prototype
31184 var Layout = function Layout(options) {
31185 this.options = options;
31186 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31187
31188 if (!plainObject(this._private)) {
31189 this._private = {};
31190 }
31191
31192 this._private.cy = options.cy;
31193 this._private.listeners = [];
31194 this.createEmitter();
31195 };
31196
31197 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31198 var optLayoutFns = [];
31199
31200 for (var i = 0; i < optLayoutFns.length; i++) {
31201 var fnName = optLayoutFns[i];
31202
31203 layoutProto[fnName] = layoutProto[fnName] || function () {
31204 return this;
31205 };
31206 } // either .start() or .run() is defined, so autogen the other
31207
31208
31209 if (layoutProto.start && !layoutProto.run) {
31210 layoutProto.run = function () {
31211 this.start();
31212 return this;
31213 };
31214 } else if (!layoutProto.start && layoutProto.run) {
31215 layoutProto.start = function () {
31216 this.run();
31217 return this;
31218 };
31219 }
31220
31221 var regStop = registrant.prototype.stop;
31222
31223 layoutProto.stop = function () {
31224 var opts = this.options;
31225
31226 if (opts && opts.animate) {
31227 var anis = this.animations;
31228
31229 if (anis) {
31230 for (var _i = 0; _i < anis.length; _i++) {
31231 anis[_i].stop();
31232 }
31233 }
31234 }
31235
31236 if (regStop) {
31237 regStop.call(this);
31238 } else {
31239 this.emit('layoutstop');
31240 }
31241
31242 return this;
31243 };
31244
31245 if (!layoutProto.destroy) {
31246 layoutProto.destroy = function () {
31247 return this;
31248 };
31249 }
31250
31251 layoutProto.cy = function () {
31252 return this._private.cy;
31253 };
31254
31255 var getCy = function getCy(layout) {
31256 return layout._private.cy;
31257 };
31258
31259 var emitterOpts = {
31260 addEventFields: function addEventFields(layout, evt) {
31261 evt.layout = layout;
31262 evt.cy = getCy(layout);
31263 evt.target = layout;
31264 },
31265 bubble: function bubble() {
31266 return true;
31267 },
31268 parent: function parent(layout) {
31269 return getCy(layout);
31270 }
31271 };
31272 extend(layoutProto, {
31273 createEmitter: function createEmitter() {
31274 this._private.emitter = new Emitter(emitterOpts, this);
31275 return this;
31276 },
31277 emitter: function emitter() {
31278 return this._private.emitter;
31279 },
31280 on: function on(evt, cb) {
31281 this.emitter().on(evt, cb);
31282 return this;
31283 },
31284 one: function one(evt, cb) {
31285 this.emitter().one(evt, cb);
31286 return this;
31287 },
31288 once: function once(evt, cb) {
31289 this.emitter().one(evt, cb);
31290 return this;
31291 },
31292 removeListener: function removeListener(evt, cb) {
31293 this.emitter().removeListener(evt, cb);
31294 return this;
31295 },
31296 removeAllListeners: function removeAllListeners() {
31297 this.emitter().removeAllListeners();
31298 return this;
31299 },
31300 emit: function emit(evt, params) {
31301 this.emitter().emit(evt, params);
31302 return this;
31303 }
31304 });
31305 define$3.eventAliasesOn(layoutProto);
31306 ext = Layout; // replace with our wrapped layout
31307 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31308 // user registered renderers inherit from base
31309 var BaseRenderer = getExtension('renderer', 'base');
31310 var bProto = BaseRenderer.prototype;
31311 var RegistrantRenderer = registrant;
31312 var rProto = registrant.prototype;
31313
31314 var Renderer = function Renderer() {
31315 BaseRenderer.apply(this, arguments);
31316 RegistrantRenderer.apply(this, arguments);
31317 };
31318
31319 var proto = Renderer.prototype;
31320
31321 for (var pName in bProto) {
31322 var pVal = bProto[pName];
31323 var existsInR = rProto[pName] != null;
31324
31325 if (existsInR) {
31326 return overrideErr(pName);
31327 }
31328
31329 proto[pName] = pVal; // take impl from base
31330 }
31331
31332 for (var _pName in rProto) {
31333 proto[_pName] = rProto[_pName]; // take impl from registrant
31334 }
31335
31336 bProto.clientFunctions.forEach(function (name) {
31337 proto[name] = proto[name] || function () {
31338 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31339 };
31340 });
31341 ext = Renderer;
31342 }
31343
31344 return setMap({
31345 map: extensions,
31346 keys: [type, name],
31347 value: ext
31348 });
31349}
31350
31351function getExtension(type, name) {
31352 return getMap({
31353 map: extensions,
31354 keys: [type, name]
31355 });
31356}
31357
31358function setModule(type, name, moduleType, moduleName, registrant) {
31359 return setMap({
31360 map: modules,
31361 keys: [type, name, moduleType, moduleName],
31362 value: registrant
31363 });
31364}
31365
31366function getModule(type, name, moduleType, moduleName) {
31367 return getMap({
31368 map: modules,
31369 keys: [type, name, moduleType, moduleName]
31370 });
31371}
31372
31373var extension = function extension() {
31374 // e.g. extension('renderer', 'svg')
31375 if (arguments.length === 2) {
31376 return getExtension.apply(null, arguments);
31377 } // e.g. extension('renderer', 'svg', { ... })
31378 else if (arguments.length === 3) {
31379 return setExtension.apply(null, arguments);
31380 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31381 else if (arguments.length === 4) {
31382 return getModule.apply(null, arguments);
31383 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31384 else if (arguments.length === 5) {
31385 return setModule.apply(null, arguments);
31386 } else {
31387 error('Invalid extension access syntax');
31388 }
31389}; // allows a core instance to access extensions internally
31390
31391
31392Core.prototype.extension = extension; // included extensions
31393
31394incExts.forEach(function (group) {
31395 group.extensions.forEach(function (ext) {
31396 setExtension(group.type, ext.name, ext.impl);
31397 });
31398});
31399
31400// (useful for init)
31401
31402var Stylesheet = function Stylesheet() {
31403 if (!(this instanceof Stylesheet)) {
31404 return new Stylesheet();
31405 }
31406
31407 this.length = 0;
31408};
31409
31410var sheetfn = Stylesheet.prototype;
31411
31412sheetfn.instanceString = function () {
31413 return 'stylesheet';
31414}; // just store the selector to be parsed later
31415
31416
31417sheetfn.selector = function (selector) {
31418 var i = this.length++;
31419 this[i] = {
31420 selector: selector,
31421 properties: []
31422 };
31423 return this; // chaining
31424}; // just store the property to be parsed later
31425
31426
31427sheetfn.css = function (name, value) {
31428 var i = this.length - 1;
31429
31430 if (string(name)) {
31431 this[i].properties.push({
31432 name: name,
31433 value: value
31434 });
31435 } else if (plainObject(name)) {
31436 var map = name;
31437 var propNames = Object.keys(map);
31438
31439 for (var j = 0; j < propNames.length; j++) {
31440 var key = propNames[j];
31441 var mapVal = map[key];
31442
31443 if (mapVal == null) {
31444 continue;
31445 }
31446
31447 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31448
31449 if (prop == null) {
31450 continue;
31451 }
31452
31453 var _name = prop.name;
31454 var _value = mapVal;
31455 this[i].properties.push({
31456 name: _name,
31457 value: _value
31458 });
31459 }
31460 }
31461
31462 return this; // chaining
31463};
31464
31465sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31466
31467sheetfn.generateStyle = function (cy) {
31468 var style = new Style(cy);
31469 return this.appendToStyle(style);
31470}; // append a dummy stylesheet object on a real style object
31471
31472
31473sheetfn.appendToStyle = function (style) {
31474 for (var i = 0; i < this.length; i++) {
31475 var context = this[i];
31476 var selector = context.selector;
31477 var props = context.properties;
31478 style.selector(selector); // apply selector
31479
31480 for (var j = 0; j < props.length; j++) {
31481 var prop = props[j];
31482 style.css(prop.name, prop.value); // apply property
31483 }
31484 }
31485
31486 return style;
31487};
31488
31489var version = "3.14.1";
31490
31491var cytoscape = function cytoscape(options) {
31492 // if no options specified, use default
31493 if (options === undefined) {
31494 options = {};
31495 } // create instance
31496
31497
31498 if (plainObject(options)) {
31499 return new Core(options);
31500 } // allow for registration of extensions
31501 else if (string(options)) {
31502 return extension.apply(extension, arguments);
31503 }
31504}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31505
31506
31507cytoscape.use = function (ext) {
31508 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31509
31510 args.unshift(cytoscape); // cytoscape is first arg to ext
31511
31512 ext.apply(null, args);
31513 return this;
31514};
31515
31516cytoscape.warnings = function (bool) {
31517 return warnings(bool);
31518}; // replaced by build system
31519
31520
31521cytoscape.version = version; // expose public apis (mostly for extensions)
31522
31523cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31524
31525export default cytoscape;