UNPKG

860 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 !elementOrCollection(obj) && (Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array);
140};
141var plainObject = function plainObject(obj) {
142 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
143};
144var object = function object(obj) {
145 return obj != null && _typeof(obj) === typeofobj;
146};
147var number = function number(obj) {
148 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
149};
150var integer = function integer(obj) {
151 return number(obj) && Math.floor(obj) === obj;
152};
153var htmlElement = function htmlElement(obj) {
154 if ('undefined' === typeofhtmlele) {
155 return undefined;
156 } else {
157 return null != obj && obj instanceof HTMLElement;
158 }
159};
160var elementOrCollection = function elementOrCollection(obj) {
161 return element(obj) || collection(obj);
162};
163var element = function element(obj) {
164 return instanceStr(obj) === 'collection' && obj._private.single;
165};
166var collection = function collection(obj) {
167 return instanceStr(obj) === 'collection' && !obj._private.single;
168};
169var core = function core(obj) {
170 return instanceStr(obj) === 'core';
171};
172var stylesheet = function stylesheet(obj) {
173 return instanceStr(obj) === 'stylesheet';
174};
175var event = function event(obj) {
176 return instanceStr(obj) === 'event';
177};
178var emptyString = function emptyString(obj) {
179 if (obj === undefined || obj === null) {
180 // null is empty
181 return true;
182 } else if (obj === '' || obj.match(/^\s+$/)) {
183 return true; // empty string is empty
184 }
185
186 return false; // otherwise, we don't know what we've got
187};
188var domElement = function domElement(obj) {
189 if (typeof HTMLElement === 'undefined') {
190 return false; // we're not in a browser so it doesn't matter
191 } else {
192 return obj instanceof HTMLElement;
193 }
194};
195var boundingBox = function boundingBox(obj) {
196 return plainObject(obj) && number(obj.x1) && number(obj.x2) && number(obj.y1) && number(obj.y2);
197};
198var promise = function promise(obj) {
199 return object(obj) && fn(obj.then);
200};
201var ms = function ms() {
202 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
203}; // probably a better way to detect this...
204
205var memoize = function memoize(fn, keyFn) {
206 if (!keyFn) {
207 keyFn = function keyFn() {
208 if (arguments.length === 1) {
209 return arguments[0];
210 } else if (arguments.length === 0) {
211 return 'undefined';
212 }
213
214 var args = [];
215
216 for (var i = 0; i < arguments.length; i++) {
217 args.push(arguments[i]);
218 }
219
220 return args.join('$');
221 };
222 }
223
224 var memoizedFn = function memoizedFn() {
225 var self = this;
226 var args = arguments;
227 var ret;
228 var k = keyFn.apply(self, args);
229 var cache = memoizedFn.cache;
230
231 if (!(ret = cache[k])) {
232 ret = cache[k] = fn.apply(self, args);
233 }
234
235 return ret;
236 };
237
238 memoizedFn.cache = {};
239 return memoizedFn;
240};
241
242var camel2dash = memoize(function (str) {
243 return str.replace(/([A-Z])/g, function (v) {
244 return '-' + v.toLowerCase();
245 });
246});
247var dash2camel = memoize(function (str) {
248 return str.replace(/(-\w)/g, function (v) {
249 return v[1].toUpperCase();
250 });
251});
252var prependCamel = memoize(function (prefix, str) {
253 return prefix + str[0].toUpperCase() + str.substring(1);
254}, function (prefix, str) {
255 return prefix + '$' + str;
256});
257var capitalize = function capitalize(str) {
258 if (emptyString(str)) {
259 return str;
260 }
261
262 return str.charAt(0).toUpperCase() + str.substring(1);
263};
264
265var number$1 = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
266var rgba = 'rgb[a]?\\((' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)(?:\\s*,\\s*(' + number$1 + '))?\\)';
267var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
268var hsla = 'hsl[a]?\\((' + number$1 + ')\\s*,\\s*(' + number$1 + '[%])\\s*,\\s*(' + number$1 + '[%])(?:\\s*,\\s*(' + number$1 + '))?\\)';
269var hslaNoBackRefs = 'hsl[a]?\\((?:' + number$1 + ')\\s*,\\s*(?:' + number$1 + '[%])\\s*,\\s*(?:' + number$1 + '[%])(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
270var hex3 = '\\#[0-9a-fA-F]{3}';
271var hex6 = '\\#[0-9a-fA-F]{6}';
272
273var ascending = function ascending(a, b) {
274 if (a < b) {
275 return -1;
276 } else if (a > b) {
277 return 1;
278 } else {
279 return 0;
280 }
281};
282var descending = function descending(a, b) {
283 return -1 * ascending(a, b);
284};
285
286var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
287 var args = arguments;
288
289 for (var i = 1; i < args.length; i++) {
290 var obj = args[i];
291
292 if (obj == null) {
293 continue;
294 }
295
296 var keys = Object.keys(obj);
297
298 for (var j = 0; j < keys.length; j++) {
299 var k = keys[j];
300 tgt[k] = obj[k];
301 }
302 }
303
304 return tgt;
305};
306
307var hex2tuple = function hex2tuple(hex) {
308 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
309 return;
310 }
311
312 var shortHex = hex.length === 4;
313 var r, g, b;
314 var base = 16;
315
316 if (shortHex) {
317 r = parseInt(hex[1] + hex[1], base);
318 g = parseInt(hex[2] + hex[2], base);
319 b = parseInt(hex[3] + hex[3], base);
320 } else {
321 r = parseInt(hex[1] + hex[2], base);
322 g = parseInt(hex[3] + hex[4], base);
323 b = parseInt(hex[5] + hex[6], base);
324 }
325
326 return [r, g, b];
327}; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
328
329var hsl2tuple = function hsl2tuple(hsl) {
330 var ret;
331 var h, s, l, a, r, g, b;
332
333 function hue2rgb(p, q, t) {
334 if (t < 0) t += 1;
335 if (t > 1) t -= 1;
336 if (t < 1 / 6) return p + (q - p) * 6 * t;
337 if (t < 1 / 2) return q;
338 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
339 return p;
340 }
341
342 var m = new RegExp('^' + hsla + '$').exec(hsl);
343
344 if (m) {
345 // get hue
346 h = parseInt(m[1]);
347
348 if (h < 0) {
349 h = (360 - -1 * h % 360) % 360;
350 } else if (h > 360) {
351 h = h % 360;
352 }
353
354 h /= 360; // normalise on [0, 1]
355
356 s = parseFloat(m[2]);
357
358 if (s < 0 || s > 100) {
359 return;
360 } // saturation is [0, 100]
361
362
363 s = s / 100; // normalise on [0, 1]
364
365 l = parseFloat(m[3]);
366
367 if (l < 0 || l > 100) {
368 return;
369 } // lightness is [0, 100]
370
371
372 l = l / 100; // normalise on [0, 1]
373
374 a = m[4];
375
376 if (a !== undefined) {
377 a = parseFloat(a);
378
379 if (a < 0 || a > 1) {
380 return;
381 } // alpha is [0, 1]
382
383 } // now, convert to rgb
384 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
385
386
387 if (s === 0) {
388 r = g = b = Math.round(l * 255); // achromatic
389 } else {
390 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
391 var p = 2 * l - q;
392 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
393 g = Math.round(255 * hue2rgb(p, q, h));
394 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
395 }
396
397 ret = [r, g, b, a];
398 }
399
400 return ret;
401}; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
402
403var rgb2tuple = function rgb2tuple(rgb) {
404 var ret;
405 var m = new RegExp('^' + rgba + '$').exec(rgb);
406
407 if (m) {
408 ret = [];
409 var isPct = [];
410
411 for (var i = 1; i <= 3; i++) {
412 var channel = m[i];
413
414 if (channel[channel.length - 1] === '%') {
415 isPct[i] = true;
416 }
417
418 channel = parseFloat(channel);
419
420 if (isPct[i]) {
421 channel = channel / 100 * 255; // normalise to [0, 255]
422 }
423
424 if (channel < 0 || channel > 255) {
425 return;
426 } // invalid channel value
427
428
429 ret.push(Math.floor(channel));
430 }
431
432 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
433 var allArePct = isPct[1] && isPct[2] && isPct[3];
434
435 if (atLeastOneIsPct && !allArePct) {
436 return;
437 } // must all be percent values if one is
438
439
440 var alpha = m[4];
441
442 if (alpha !== undefined) {
443 alpha = parseFloat(alpha);
444
445 if (alpha < 0 || alpha > 1) {
446 return;
447 } // invalid alpha value
448
449
450 ret.push(alpha);
451 }
452 }
453
454 return ret;
455};
456var colorname2tuple = function colorname2tuple(color) {
457 return colors[color.toLowerCase()];
458};
459var color2tuple = function color2tuple(color) {
460 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
461};
462var colors = {
463 // special colour names
464 transparent: [0, 0, 0, 0],
465 // NB alpha === 0
466 // regular colours
467 aliceblue: [240, 248, 255],
468 antiquewhite: [250, 235, 215],
469 aqua: [0, 255, 255],
470 aquamarine: [127, 255, 212],
471 azure: [240, 255, 255],
472 beige: [245, 245, 220],
473 bisque: [255, 228, 196],
474 black: [0, 0, 0],
475 blanchedalmond: [255, 235, 205],
476 blue: [0, 0, 255],
477 blueviolet: [138, 43, 226],
478 brown: [165, 42, 42],
479 burlywood: [222, 184, 135],
480 cadetblue: [95, 158, 160],
481 chartreuse: [127, 255, 0],
482 chocolate: [210, 105, 30],
483 coral: [255, 127, 80],
484 cornflowerblue: [100, 149, 237],
485 cornsilk: [255, 248, 220],
486 crimson: [220, 20, 60],
487 cyan: [0, 255, 255],
488 darkblue: [0, 0, 139],
489 darkcyan: [0, 139, 139],
490 darkgoldenrod: [184, 134, 11],
491 darkgray: [169, 169, 169],
492 darkgreen: [0, 100, 0],
493 darkgrey: [169, 169, 169],
494 darkkhaki: [189, 183, 107],
495 darkmagenta: [139, 0, 139],
496 darkolivegreen: [85, 107, 47],
497 darkorange: [255, 140, 0],
498 darkorchid: [153, 50, 204],
499 darkred: [139, 0, 0],
500 darksalmon: [233, 150, 122],
501 darkseagreen: [143, 188, 143],
502 darkslateblue: [72, 61, 139],
503 darkslategray: [47, 79, 79],
504 darkslategrey: [47, 79, 79],
505 darkturquoise: [0, 206, 209],
506 darkviolet: [148, 0, 211],
507 deeppink: [255, 20, 147],
508 deepskyblue: [0, 191, 255],
509 dimgray: [105, 105, 105],
510 dimgrey: [105, 105, 105],
511 dodgerblue: [30, 144, 255],
512 firebrick: [178, 34, 34],
513 floralwhite: [255, 250, 240],
514 forestgreen: [34, 139, 34],
515 fuchsia: [255, 0, 255],
516 gainsboro: [220, 220, 220],
517 ghostwhite: [248, 248, 255],
518 gold: [255, 215, 0],
519 goldenrod: [218, 165, 32],
520 gray: [128, 128, 128],
521 grey: [128, 128, 128],
522 green: [0, 128, 0],
523 greenyellow: [173, 255, 47],
524 honeydew: [240, 255, 240],
525 hotpink: [255, 105, 180],
526 indianred: [205, 92, 92],
527 indigo: [75, 0, 130],
528 ivory: [255, 255, 240],
529 khaki: [240, 230, 140],
530 lavender: [230, 230, 250],
531 lavenderblush: [255, 240, 245],
532 lawngreen: [124, 252, 0],
533 lemonchiffon: [255, 250, 205],
534 lightblue: [173, 216, 230],
535 lightcoral: [240, 128, 128],
536 lightcyan: [224, 255, 255],
537 lightgoldenrodyellow: [250, 250, 210],
538 lightgray: [211, 211, 211],
539 lightgreen: [144, 238, 144],
540 lightgrey: [211, 211, 211],
541 lightpink: [255, 182, 193],
542 lightsalmon: [255, 160, 122],
543 lightseagreen: [32, 178, 170],
544 lightskyblue: [135, 206, 250],
545 lightslategray: [119, 136, 153],
546 lightslategrey: [119, 136, 153],
547 lightsteelblue: [176, 196, 222],
548 lightyellow: [255, 255, 224],
549 lime: [0, 255, 0],
550 limegreen: [50, 205, 50],
551 linen: [250, 240, 230],
552 magenta: [255, 0, 255],
553 maroon: [128, 0, 0],
554 mediumaquamarine: [102, 205, 170],
555 mediumblue: [0, 0, 205],
556 mediumorchid: [186, 85, 211],
557 mediumpurple: [147, 112, 219],
558 mediumseagreen: [60, 179, 113],
559 mediumslateblue: [123, 104, 238],
560 mediumspringgreen: [0, 250, 154],
561 mediumturquoise: [72, 209, 204],
562 mediumvioletred: [199, 21, 133],
563 midnightblue: [25, 25, 112],
564 mintcream: [245, 255, 250],
565 mistyrose: [255, 228, 225],
566 moccasin: [255, 228, 181],
567 navajowhite: [255, 222, 173],
568 navy: [0, 0, 128],
569 oldlace: [253, 245, 230],
570 olive: [128, 128, 0],
571 olivedrab: [107, 142, 35],
572 orange: [255, 165, 0],
573 orangered: [255, 69, 0],
574 orchid: [218, 112, 214],
575 palegoldenrod: [238, 232, 170],
576 palegreen: [152, 251, 152],
577 paleturquoise: [175, 238, 238],
578 palevioletred: [219, 112, 147],
579 papayawhip: [255, 239, 213],
580 peachpuff: [255, 218, 185],
581 peru: [205, 133, 63],
582 pink: [255, 192, 203],
583 plum: [221, 160, 221],
584 powderblue: [176, 224, 230],
585 purple: [128, 0, 128],
586 red: [255, 0, 0],
587 rosybrown: [188, 143, 143],
588 royalblue: [65, 105, 225],
589 saddlebrown: [139, 69, 19],
590 salmon: [250, 128, 114],
591 sandybrown: [244, 164, 96],
592 seagreen: [46, 139, 87],
593 seashell: [255, 245, 238],
594 sienna: [160, 82, 45],
595 silver: [192, 192, 192],
596 skyblue: [135, 206, 235],
597 slateblue: [106, 90, 205],
598 slategray: [112, 128, 144],
599 slategrey: [112, 128, 144],
600 snow: [255, 250, 250],
601 springgreen: [0, 255, 127],
602 steelblue: [70, 130, 180],
603 tan: [210, 180, 140],
604 teal: [0, 128, 128],
605 thistle: [216, 191, 216],
606 tomato: [255, 99, 71],
607 turquoise: [64, 224, 208],
608 violet: [238, 130, 238],
609 wheat: [245, 222, 179],
610 white: [255, 255, 255],
611 whitesmoke: [245, 245, 245],
612 yellow: [255, 255, 0],
613 yellowgreen: [154, 205, 50]
614};
615
616var setMap = function setMap(options) {
617 var obj = options.map;
618 var keys = options.keys;
619 var l = keys.length;
620
621 for (var i = 0; i < l; i++) {
622 var key = keys[i];
623
624 if (plainObject(key)) {
625 throw Error('Tried to set map with object key');
626 }
627
628 if (i < keys.length - 1) {
629 // extend the map if necessary
630 if (obj[key] == null) {
631 obj[key] = {};
632 }
633
634 obj = obj[key];
635 } else {
636 // set the value
637 obj[key] = options.value;
638 }
639 }
640}; // gets the value in a map even if it's not built in places
641
642var getMap = function getMap(options) {
643 var obj = options.map;
644 var keys = options.keys;
645 var l = keys.length;
646
647 for (var i = 0; i < l; i++) {
648 var key = keys[i];
649
650 if (plainObject(key)) {
651 throw Error('Tried to get map with object key');
652 }
653
654 obj = obj[key];
655
656 if (obj == null) {
657 return obj;
658 }
659 }
660
661 return obj;
662}; // deletes the entry in the map
663
664var performance = window$1 ? window$1.performance : null;
665var pnow = performance && performance.now ? function () {
666 return performance.now();
667} : function () {
668 return Date.now();
669};
670
671var raf = function () {
672 if (window$1) {
673 if (window$1.requestAnimationFrame) {
674 return function (fn) {
675 window$1.requestAnimationFrame(fn);
676 };
677 } else if (window$1.mozRequestAnimationFrame) {
678 return function (fn) {
679 window$1.mozRequestAnimationFrame(fn);
680 };
681 } else if (window$1.webkitRequestAnimationFrame) {
682 return function (fn) {
683 window$1.webkitRequestAnimationFrame(fn);
684 };
685 } else if (window$1.msRequestAnimationFrame) {
686 return function (fn) {
687 window$1.msRequestAnimationFrame(fn);
688 };
689 }
690 }
691
692 return function (fn) {
693 if (fn) {
694 setTimeout(function () {
695 fn(pnow());
696 }, 1000 / 60);
697 }
698 };
699}();
700
701var requestAnimationFrame = function requestAnimationFrame(fn) {
702 return raf(fn);
703};
704var performanceNow = pnow;
705
706var DEFAULT_HASH_SEED = 9261;
707var K = 65599; // 37 also works pretty well
708
709var DEFAULT_HASH_SEED_ALT = 5381;
710var hashIterableInts = function hashIterableInts(iterator) {
711 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
712 // sdbm/string-hash
713 var hash = seed;
714 var entry;
715
716 for (;;) {
717 entry = iterator.next();
718
719 if (entry.done) {
720 break;
721 }
722
723 hash = hash * K + entry.value | 0;
724 }
725
726 return hash;
727};
728var hashInt = function hashInt(num) {
729 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
730 // sdbm/string-hash
731 return seed * K + num | 0;
732};
733var hashIntAlt = function hashIntAlt(num) {
734 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
735 // djb2/string-hash
736 return (seed << 5) + seed + num | 0;
737};
738var combineHashes = function combineHashes(hash1, hash2) {
739 return hash1 * 0x200000 + hash2;
740};
741var combineHashesArray = function combineHashesArray(hashes) {
742 return hashes[0] * 0x200000 + hashes[1];
743};
744var hashArrays = function hashArrays(hashes1, hashes2) {
745 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
746};
747var hashIntsArray = function hashIntsArray(ints, seed) {
748 var entry = {
749 value: 0,
750 done: false
751 };
752 var i = 0;
753 var length = ints.length;
754 var iterator = {
755 next: function next() {
756 if (i < length) {
757 entry.value = ints[i++];
758 } else {
759 entry.done = true;
760 }
761
762 return entry;
763 }
764 };
765 return hashIterableInts(iterator, seed);
766};
767var hashString = function hashString(str, seed) {
768 var entry = {
769 value: 0,
770 done: false
771 };
772 var i = 0;
773 var length = str.length;
774 var iterator = {
775 next: function next() {
776 if (i < length) {
777 entry.value = str.charCodeAt(i++);
778 } else {
779 entry.done = true;
780 }
781
782 return entry;
783 }
784 };
785 return hashIterableInts(iterator, seed);
786};
787var hashStrings = function hashStrings() {
788 return hashStringsArray(arguments);
789};
790var hashStringsArray = function hashStringsArray(strs) {
791 var hash;
792
793 for (var i = 0; i < strs.length; i++) {
794 var str = strs[i];
795
796 if (i === 0) {
797 hash = hashString(str);
798 } else {
799 hash = hashString(str, hash);
800 }
801 }
802
803 return hash;
804};
805
806/*global console */
807var warningsEnabled = true;
808var warnSupported = console.warn != null; // eslint-disable-line no-console
809
810var traceSupported = console.trace != null; // eslint-disable-line no-console
811
812var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
813var trueify = function trueify() {
814 return true;
815};
816var falsify = function falsify() {
817 return false;
818};
819var zeroify = function zeroify() {
820 return 0;
821};
822var noop = function noop() {};
823var error = function error(msg) {
824 throw new Error(msg);
825};
826var warnings = function warnings(enabled) {
827 if (enabled !== undefined) {
828 warningsEnabled = !!enabled;
829 } else {
830 return warningsEnabled;
831 }
832};
833var warn = function warn(msg) {
834 /* eslint-disable no-console */
835 if (!warnings()) {
836 return;
837 }
838
839 if (warnSupported) {
840 console.warn(msg);
841 } else {
842 console.log(msg);
843
844 if (traceSupported) {
845 console.trace();
846 }
847 }
848};
849/* eslint-enable */
850
851var clone = function clone(obj) {
852 return extend({}, obj);
853}; // gets a shallow copy of the argument
854
855var copy = function copy(obj) {
856 if (obj == null) {
857 return obj;
858 }
859
860 if (array(obj)) {
861 return obj.slice();
862 } else if (plainObject(obj)) {
863 return clone(obj);
864 } else {
865 return obj;
866 }
867};
868var copyArray = function copyArray(arr) {
869 return arr.slice();
870};
871var uuid = function uuid(a, b
872/* placeholders */
873) {
874 for ( // loop :)
875 b = a = ''; // b - result , a - numeric letiable
876 a++ < 36; //
877 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
878 ? // return a random number or 4
879 (a ^ 15 // if "a" is not 15
880 ? // genetate a random number from 0 to 15
881 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
882 : 4 // otherwise 4
883 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
884 ) {
885 }
886
887 return b;
888};
889var _staticEmptyObject = {};
890var staticEmptyObject = function staticEmptyObject() {
891 return _staticEmptyObject;
892};
893var defaults = function defaults(_defaults) {
894 var keys = Object.keys(_defaults);
895 return function (opts) {
896 var filledOpts = {};
897
898 for (var i = 0; i < keys.length; i++) {
899 var key = keys[i];
900 var optVal = opts == null ? undefined : opts[key];
901 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
902 }
903
904 return filledOpts;
905 };
906};
907var removeFromArray = function removeFromArray(arr, ele, manyCopies) {
908 for (var i = arr.length; i >= 0; i--) {
909 if (arr[i] === ele) {
910 arr.splice(i, 1);
911
912 if (!manyCopies) {
913 break;
914 }
915 }
916 }
917};
918var clearArray = function clearArray(arr) {
919 arr.splice(0, arr.length);
920};
921var push = function push(arr, otherArr) {
922 for (var i = 0; i < otherArr.length; i++) {
923 var el = otherArr[i];
924 arr.push(el);
925 }
926};
927var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
928 if (prefix) {
929 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
930 }
931
932 return obj[propName];
933};
934var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
935 if (prefix) {
936 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
937 }
938
939 obj[propName] = value;
940};
941
942/* global Map */
943var ObjectMap =
944/*#__PURE__*/
945function () {
946 function ObjectMap() {
947 _classCallCheck(this, ObjectMap);
948
949 this._obj = {};
950 }
951
952 _createClass(ObjectMap, [{
953 key: "set",
954 value: function set(key, val) {
955 this._obj[key] = val;
956 return this;
957 }
958 }, {
959 key: "delete",
960 value: function _delete(key) {
961 this._obj[key] = undefined;
962 return this;
963 }
964 }, {
965 key: "clear",
966 value: function clear() {
967 this._obj = {};
968 }
969 }, {
970 key: "has",
971 value: function has(key) {
972 return this._obj[key] !== undefined;
973 }
974 }, {
975 key: "get",
976 value: function get(key) {
977 return this._obj[key];
978 }
979 }]);
980
981 return ObjectMap;
982}();
983
984var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
985
986/* global Set */
987var undef = "undefined" ;
988
989var ObjectSet =
990/*#__PURE__*/
991function () {
992 function ObjectSet(arrayOrObjectSet) {
993 _classCallCheck(this, ObjectSet);
994
995 this._obj = Object.create(null);
996 this.size = 0;
997
998 if (arrayOrObjectSet != null) {
999 var arr;
1000
1001 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1002 arr = arrayOrObjectSet.toArray();
1003 } else {
1004 arr = arrayOrObjectSet;
1005 }
1006
1007 for (var i = 0; i < arr.length; i++) {
1008 this.add(arr[i]);
1009 }
1010 }
1011 }
1012
1013 _createClass(ObjectSet, [{
1014 key: "instanceString",
1015 value: function instanceString() {
1016 return 'set';
1017 }
1018 }, {
1019 key: "add",
1020 value: function add(val) {
1021 var o = this._obj;
1022
1023 if (o[val] !== 1) {
1024 o[val] = 1;
1025 this.size++;
1026 }
1027 }
1028 }, {
1029 key: "delete",
1030 value: function _delete(val) {
1031 var o = this._obj;
1032
1033 if (o[val] === 1) {
1034 o[val] = 0;
1035 this.size--;
1036 }
1037 }
1038 }, {
1039 key: "clear",
1040 value: function clear() {
1041 this._obj = Object.create(null);
1042 }
1043 }, {
1044 key: "has",
1045 value: function has(val) {
1046 return this._obj[val] === 1;
1047 }
1048 }, {
1049 key: "toArray",
1050 value: function toArray() {
1051 var _this = this;
1052
1053 return Object.keys(this._obj).filter(function (key) {
1054 return _this.has(key);
1055 });
1056 }
1057 }, {
1058 key: "forEach",
1059 value: function forEach(callback, thisArg) {
1060 return this.toArray().forEach(callback, thisArg);
1061 }
1062 }]);
1063
1064 return ObjectSet;
1065}();
1066
1067var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1068
1069var Element = function Element(cy, params) {
1070 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1071
1072 if (cy === undefined || params === undefined || !core(cy)) {
1073 error('An element must have a core reference and parameters set');
1074 return;
1075 }
1076
1077 var group = params.group; // try to automatically infer the group if unspecified
1078
1079 if (group == null) {
1080 if (params.data && params.data.source != null && params.data.target != null) {
1081 group = 'edges';
1082 } else {
1083 group = 'nodes';
1084 }
1085 } // validate group
1086
1087
1088 if (group !== 'nodes' && group !== 'edges') {
1089 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1090 return;
1091 } // make the element array-like, just like a collection
1092
1093
1094 this.length = 1;
1095 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1096
1097 var _p = this._private = {
1098 cy: cy,
1099 single: true,
1100 // indicates this is an element
1101 data: params.data || {},
1102 // data object
1103 position: params.position || {
1104 x: 0,
1105 y: 0
1106 },
1107 // (x, y) position pair
1108 autoWidth: undefined,
1109 // width and height of nodes calculated by the renderer when set to special 'auto' value
1110 autoHeight: undefined,
1111 autoPadding: undefined,
1112 compoundBoundsClean: false,
1113 // whether the compound dimensions need to be recalculated the next time dimensions are read
1114 listeners: [],
1115 // array of bound listeners
1116 group: group,
1117 // string; 'nodes' or 'edges'
1118 style: {},
1119 // properties as set by the style
1120 rstyle: {},
1121 // properties for style sent from the renderer to the core
1122 styleCxts: [],
1123 // applied style contexts from the styler
1124 styleKeys: {},
1125 // per-group keys of style property values
1126 removed: true,
1127 // whether it's inside the vis; true if removed (set true here since we call restore)
1128 selected: params.selected ? true : false,
1129 // whether it's selected
1130 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1131 // whether it's selectable
1132 locked: params.locked ? true : false,
1133 // whether the element is locked (cannot be moved)
1134 grabbed: false,
1135 // whether the element is grabbed by the mouse; renderer sets this privately
1136 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1137 // whether the element can be grabbed
1138 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1139 // whether the element has passthrough panning enabled
1140 active: false,
1141 // whether the element is active from user interaction
1142 classes: new Set$1(),
1143 // map ( className => true )
1144 animation: {
1145 // object for currently-running animations
1146 current: [],
1147 queue: []
1148 },
1149 rscratch: {},
1150 // object in which the renderer can store information
1151 scratch: params.scratch || {},
1152 // scratch objects
1153 edges: [],
1154 // array of connected edges
1155 children: [],
1156 // array of children
1157 parent: null,
1158 // parent ref
1159 traversalCache: {},
1160 // cache of output of traversal functions
1161 backgrounding: false,
1162 // whether background images are loading
1163 bbCache: null,
1164 // cache of the current bounding box
1165 bbCacheShift: {
1166 x: 0,
1167 y: 0
1168 },
1169 // shift applied to cached bb to be applied on next get
1170 bodyBounds: null,
1171 // bounds cache of element body, w/o overlay
1172 overlayBounds: null,
1173 // bounds cache of element body, including overlay
1174 labelBounds: {
1175 // bounds cache of labels
1176 all: null,
1177 source: null,
1178 target: null,
1179 main: null
1180 },
1181 arrowBounds: {
1182 // bounds cache of edge arrows
1183 source: null,
1184 target: null,
1185 'mid-source': null,
1186 'mid-target': null
1187 }
1188 };
1189
1190 if (_p.position.x == null) {
1191 _p.position.x = 0;
1192 }
1193
1194 if (_p.position.y == null) {
1195 _p.position.y = 0;
1196 } // renderedPosition overrides if specified
1197
1198
1199 if (params.renderedPosition) {
1200 var rpos = params.renderedPosition;
1201 var pan = cy.pan();
1202 var zoom = cy.zoom();
1203 _p.position = {
1204 x: (rpos.x - pan.x) / zoom,
1205 y: (rpos.y - pan.y) / zoom
1206 };
1207 }
1208
1209 var classes = [];
1210
1211 if (array(params.classes)) {
1212 classes = params.classes;
1213 } else if (string(params.classes)) {
1214 classes = params.classes.split(/\s+/);
1215 }
1216
1217 for (var i = 0, l = classes.length; i < l; i++) {
1218 var cls = classes[i];
1219
1220 if (!cls || cls === '') {
1221 continue;
1222 }
1223
1224 _p.classes.add(cls);
1225 }
1226
1227 this.createEmitter();
1228 var bypass = params.style || params.css;
1229
1230 if (bypass) {
1231 warn('Setting a `style` bypass at element creation is deprecated');
1232 this.style(bypass);
1233 }
1234
1235 if (restore === undefined || restore) {
1236 this.restore();
1237 }
1238};
1239
1240var defineSearch = function defineSearch(params) {
1241 params = {
1242 bfs: params.bfs || !params.dfs,
1243 dfs: params.dfs || !params.bfs
1244 }; // from pseudocode on wikipedia
1245
1246 return function searchFn(roots, fn$1, directed) {
1247 var options;
1248
1249 if (plainObject(roots) && !elementOrCollection(roots)) {
1250 options = roots;
1251 roots = options.roots || options.root;
1252 fn$1 = options.visit;
1253 directed = options.directed;
1254 }
1255
1256 directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
1257 fn$1 = fn(fn$1) ? fn$1 : function () {};
1258 var cy = this._private.cy;
1259 var v = roots = string(roots) ? this.filter(roots) : roots;
1260 var Q = [];
1261 var connectedNodes = [];
1262 var connectedBy = {};
1263 var id2depth = {};
1264 var V = {};
1265 var j = 0;
1266 var found;
1267
1268 var _this$byGroup = this.byGroup(),
1269 nodes = _this$byGroup.nodes,
1270 edges = _this$byGroup.edges; // enqueue v
1271
1272
1273 for (var i = 0; i < v.length; i++) {
1274 var vi = v[i];
1275 var viId = vi.id();
1276
1277 if (vi.isNode()) {
1278 Q.unshift(vi);
1279
1280 if (params.bfs) {
1281 V[viId] = true;
1282 connectedNodes.push(vi);
1283 }
1284
1285 id2depth[viId] = 0;
1286 }
1287 }
1288
1289 var _loop2 = function _loop2() {
1290 var v = params.bfs ? Q.shift() : Q.pop();
1291 var vId = v.id();
1292
1293 if (params.dfs) {
1294 if (V[vId]) {
1295 return "continue";
1296 }
1297
1298 V[vId] = true;
1299 connectedNodes.push(v);
1300 }
1301
1302 var depth = id2depth[vId];
1303 var prevEdge = connectedBy[vId];
1304 var src = prevEdge != null ? prevEdge.source() : null;
1305 var tgt = prevEdge != null ? prevEdge.target() : null;
1306 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1307 var ret = void 0;
1308 ret = fn$1(v, prevEdge, prevNode, j++, depth);
1309
1310 if (ret === true) {
1311 found = v;
1312 return "break";
1313 }
1314
1315 if (ret === false) {
1316 return "break";
1317 }
1318
1319 var vwEdges = v.connectedEdges().filter(function (e) {
1320 return (!directed || e.source().same(v)) && edges.has(e);
1321 });
1322
1323 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1324 var e = vwEdges[_i2];
1325 var w = e.connectedNodes().filter(function (n) {
1326 return !n.same(v) && nodes.has(n);
1327 });
1328 var wId = w.id();
1329
1330 if (w.length !== 0 && !V[wId]) {
1331 w = w[0];
1332 Q.push(w);
1333
1334 if (params.bfs) {
1335 V[wId] = true;
1336 connectedNodes.push(w);
1337 }
1338
1339 connectedBy[wId] = e;
1340 id2depth[wId] = id2depth[vId] + 1;
1341 }
1342 }
1343 };
1344
1345 _loop: while (Q.length !== 0) {
1346 var _ret = _loop2();
1347
1348 switch (_ret) {
1349 case "continue":
1350 continue;
1351
1352 case "break":
1353 break _loop;
1354 }
1355 }
1356
1357 var connectedEles = cy.collection();
1358
1359 for (var _i = 0; _i < connectedNodes.length; _i++) {
1360 var node = connectedNodes[_i];
1361 var edge = connectedBy[node.id()];
1362
1363 if (edge != null) {
1364 connectedEles.push(edge);
1365 }
1366
1367 connectedEles.push(node);
1368 }
1369
1370 return {
1371 path: cy.collection(connectedEles),
1372 found: cy.collection(found)
1373 };
1374 };
1375}; // search, spanning trees, etc
1376
1377
1378var elesfn = {
1379 breadthFirstSearch: defineSearch({
1380 bfs: true
1381 }),
1382 depthFirstSearch: defineSearch({
1383 dfs: true
1384 })
1385}; // nice, short mathemathical alias
1386
1387elesfn.bfs = elesfn.breadthFirstSearch;
1388elesfn.dfs = elesfn.depthFirstSearch;
1389
1390var dijkstraDefaults = defaults({
1391 root: null,
1392 weight: function weight(edge) {
1393 return 1;
1394 },
1395 directed: false
1396});
1397var elesfn$1 = {
1398 dijkstra: function dijkstra(options) {
1399 if (!plainObject(options)) {
1400 var args = arguments;
1401 options = {
1402 root: args[0],
1403 weight: args[1],
1404 directed: args[2]
1405 };
1406 }
1407
1408 var _dijkstraDefaults = dijkstraDefaults(options),
1409 root = _dijkstraDefaults.root,
1410 weight = _dijkstraDefaults.weight,
1411 directed = _dijkstraDefaults.directed;
1412
1413 var eles = this;
1414 var weightFn = weight;
1415 var source = string(root) ? this.filter(root)[0] : root[0];
1416 var dist = {};
1417 var prev = {};
1418 var knownDist = {};
1419
1420 var _this$byGroup = this.byGroup(),
1421 nodes = _this$byGroup.nodes,
1422 edges = _this$byGroup.edges;
1423
1424 edges.unmergeBy(function (ele) {
1425 return ele.isLoop();
1426 });
1427
1428 var getDist = function getDist(node) {
1429 return dist[node.id()];
1430 };
1431
1432 var setDist = function setDist(node, d) {
1433 dist[node.id()] = d;
1434 Q.updateItem(node);
1435 };
1436
1437 var Q = new Heap(function (a, b) {
1438 return getDist(a) - getDist(b);
1439 });
1440
1441 for (var i = 0; i < nodes.length; i++) {
1442 var node = nodes[i];
1443 dist[node.id()] = node.same(source) ? 0 : Infinity;
1444 Q.push(node);
1445 }
1446
1447 var distBetween = function distBetween(u, v) {
1448 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
1449 var smallestDistance = Infinity;
1450 var smallestEdge;
1451
1452 for (var _i = 0; _i < uvs.length; _i++) {
1453 var edge = uvs[_i];
1454
1455 var _weight = weightFn(edge);
1456
1457 if (_weight < smallestDistance || !smallestEdge) {
1458 smallestDistance = _weight;
1459 smallestEdge = edge;
1460 }
1461 }
1462
1463 return {
1464 edge: smallestEdge,
1465 dist: smallestDistance
1466 };
1467 };
1468
1469 while (Q.size() > 0) {
1470 var u = Q.pop();
1471 var smalletsDist = getDist(u);
1472 var uid = u.id();
1473 knownDist[uid] = smalletsDist;
1474
1475 if (smalletsDist === Infinity) {
1476 continue;
1477 }
1478
1479 var neighbors = u.neighborhood().intersect(nodes);
1480
1481 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
1482 var v = neighbors[_i2];
1483 var vid = v.id();
1484 var vDist = distBetween(u, v);
1485 var alt = smalletsDist + vDist.dist;
1486
1487 if (alt < getDist(v)) {
1488 setDist(v, alt);
1489 prev[vid] = {
1490 node: u,
1491 edge: vDist.edge
1492 };
1493 }
1494 } // for
1495
1496 } // while
1497
1498
1499 return {
1500 distanceTo: function distanceTo(node) {
1501 var target = string(node) ? nodes.filter(node)[0] : node[0];
1502 return knownDist[target.id()];
1503 },
1504 pathTo: function pathTo(node) {
1505 var target = string(node) ? nodes.filter(node)[0] : node[0];
1506 var S = [];
1507 var u = target;
1508 var uid = u.id();
1509
1510 if (target.length > 0) {
1511 S.unshift(target);
1512
1513 while (prev[uid]) {
1514 var p = prev[uid];
1515 S.unshift(p.edge);
1516 S.unshift(p.node);
1517 u = p.node;
1518 uid = u.id();
1519 }
1520 }
1521
1522 return eles.spawn(S);
1523 }
1524 };
1525 }
1526};
1527
1528var elesfn$2 = {
1529 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
1530 // implemented from pseudocode from wikipedia
1531 kruskal: function kruskal(weightFn) {
1532 weightFn = weightFn || function (edge) {
1533 return 1;
1534 };
1535
1536 var _this$byGroup = this.byGroup(),
1537 nodes = _this$byGroup.nodes,
1538 edges = _this$byGroup.edges;
1539
1540 var numNodes = nodes.length;
1541 var forest = new Array(numNodes);
1542 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
1543
1544 var findSetIndex = function findSetIndex(ele) {
1545 for (var i = 0; i < forest.length; i++) {
1546 var eles = forest[i];
1547
1548 if (eles.has(ele)) {
1549 return i;
1550 }
1551 }
1552 }; // start with one forest per node
1553
1554
1555 for (var i = 0; i < numNodes; i++) {
1556 forest[i] = this.spawn(nodes[i]);
1557 }
1558
1559 var S = edges.sort(function (a, b) {
1560 return weightFn(a) - weightFn(b);
1561 });
1562
1563 for (var _i = 0; _i < S.length; _i++) {
1564 var edge = S[_i];
1565 var u = edge.source()[0];
1566 var v = edge.target()[0];
1567 var setUIndex = findSetIndex(u);
1568 var setVIndex = findSetIndex(v);
1569 var setU = forest[setUIndex];
1570 var setV = forest[setVIndex];
1571
1572 if (setUIndex !== setVIndex) {
1573 A.merge(edge); // combine forests for u and v
1574
1575 setU.merge(setV);
1576 forest.splice(setVIndex, 1);
1577 }
1578 }
1579
1580 return A;
1581 }
1582};
1583
1584var aStarDefaults = defaults({
1585 root: null,
1586 goal: null,
1587 weight: function weight(edge) {
1588 return 1;
1589 },
1590 heuristic: function heuristic(edge) {
1591 return 0;
1592 },
1593 directed: false
1594});
1595var elesfn$3 = {
1596 // Implemented from pseudocode from wikipedia
1597 aStar: function aStar(options) {
1598 var cy = this.cy();
1599
1600 var _aStarDefaults = aStarDefaults(options),
1601 root = _aStarDefaults.root,
1602 goal = _aStarDefaults.goal,
1603 heuristic = _aStarDefaults.heuristic,
1604 directed = _aStarDefaults.directed,
1605 weight = _aStarDefaults.weight;
1606
1607 root = cy.collection(root)[0];
1608 goal = cy.collection(goal)[0];
1609 var sid = root.id();
1610 var tid = goal.id();
1611 var gScore = {};
1612 var fScore = {};
1613 var closedSetIds = {};
1614 var openSet = new Heap(function (a, b) {
1615 return fScore[a.id()] - fScore[b.id()];
1616 });
1617 var openSetIds = new Set$1();
1618 var cameFrom = {};
1619 var cameFromEdge = {};
1620
1621 var addToOpenSet = function addToOpenSet(ele, id) {
1622 openSet.push(ele);
1623 openSetIds.add(id);
1624 };
1625
1626 var cMin, cMinId;
1627
1628 var popFromOpenSet = function popFromOpenSet() {
1629 cMin = openSet.pop();
1630 cMinId = cMin.id();
1631 openSetIds["delete"](cMinId);
1632 };
1633
1634 var isInOpenSet = function isInOpenSet(id) {
1635 return openSetIds.has(id);
1636 };
1637
1638 addToOpenSet(root, sid);
1639 gScore[sid] = 0;
1640 fScore[sid] = heuristic(root); // Counter
1641
1642 var steps = 0; // Main loop
1643
1644 while (openSet.size() > 0) {
1645 popFromOpenSet();
1646 steps++; // If we've found our goal, then we are done
1647
1648 if (cMinId === tid) {
1649 var path = [];
1650 var pathNode = goal;
1651 var pathNodeId = tid;
1652 var pathEdge = cameFromEdge[pathNodeId];
1653
1654 for (;;) {
1655 path.unshift(pathNode);
1656
1657 if (pathEdge != null) {
1658 path.unshift(pathEdge);
1659 }
1660
1661 pathNode = cameFrom[pathNodeId];
1662
1663 if (pathNode == null) {
1664 break;
1665 }
1666
1667 pathNodeId = pathNode.id();
1668 pathEdge = cameFromEdge[pathNodeId];
1669 }
1670
1671 return {
1672 found: true,
1673 distance: gScore[cMinId],
1674 path: this.spawn(path),
1675 steps: steps
1676 };
1677 } // Add cMin to processed nodes
1678
1679
1680 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
1681 // Take into account if graph is directed or not
1682
1683 var vwEdges = cMin._private.edges;
1684
1685 for (var i = 0; i < vwEdges.length; i++) {
1686 var e = vwEdges[i]; // edge must be in set of calling eles
1687
1688 if (!this.hasElementWithId(e.id())) {
1689 continue;
1690 } // cMin must be the source of edge if directed
1691
1692
1693 if (directed && e.data('source') !== cMinId) {
1694 continue;
1695 }
1696
1697 var wSrc = e.source();
1698 var wTgt = e.target();
1699 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
1700 var wid = w.id(); // node must be in set of calling eles
1701
1702 if (!this.hasElementWithId(wid)) {
1703 continue;
1704 } // if node is in closedSet, ignore it
1705
1706
1707 if (closedSetIds[wid]) {
1708 continue;
1709 } // New tentative score for node w
1710
1711
1712 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
1713 // w not present in openSet
1714 // OR
1715 // tentative gScore is less than previous value
1716 // w not in openSet
1717
1718 if (!isInOpenSet(wid)) {
1719 gScore[wid] = tempScore;
1720 fScore[wid] = tempScore + heuristic(w);
1721 addToOpenSet(w, wid);
1722 cameFrom[wid] = cMin;
1723 cameFromEdge[wid] = e;
1724 continue;
1725 } // w already in openSet, but with greater gScore
1726
1727
1728 if (tempScore < gScore[wid]) {
1729 gScore[wid] = tempScore;
1730 fScore[wid] = tempScore + heuristic(w);
1731 cameFrom[wid] = cMin;
1732 }
1733 } // End of neighbors update
1734
1735 } // End of main loop
1736 // If we've reached here, then we've not reached our goal
1737
1738
1739 return {
1740 found: false,
1741 distance: undefined,
1742 path: undefined,
1743 steps: steps
1744 };
1745 }
1746}; // elesfn
1747
1748var floydWarshallDefaults = defaults({
1749 weight: function weight(edge) {
1750 return 1;
1751 },
1752 directed: false
1753});
1754var elesfn$4 = {
1755 // Implemented from pseudocode from wikipedia
1756 floydWarshall: function floydWarshall(options) {
1757 var cy = this.cy();
1758
1759 var _floydWarshallDefault = floydWarshallDefaults(options),
1760 weight = _floydWarshallDefault.weight,
1761 directed = _floydWarshallDefault.directed;
1762
1763 var weightFn = weight;
1764
1765 var _this$byGroup = this.byGroup(),
1766 nodes = _this$byGroup.nodes,
1767 edges = _this$byGroup.edges;
1768
1769 var N = nodes.length;
1770 var Nsq = N * N;
1771
1772 var indexOf = function indexOf(node) {
1773 return nodes.indexOf(node);
1774 };
1775
1776 var atIndex = function atIndex(i) {
1777 return nodes[i];
1778 }; // Initialize distance matrix
1779
1780
1781 var dist = new Array(Nsq);
1782
1783 for (var n = 0; n < Nsq; n++) {
1784 var j = n % N;
1785 var i = (n - j) / N;
1786
1787 if (i === j) {
1788 dist[n] = 0;
1789 } else {
1790 dist[n] = Infinity;
1791 }
1792 } // Initialize matrix used for path reconstruction
1793 // Initialize distance matrix
1794
1795
1796 var next = new Array(Nsq);
1797 var edgeNext = new Array(Nsq); // Process edges
1798
1799 for (var _i = 0; _i < edges.length; _i++) {
1800 var edge = edges[_i];
1801 var src = edge.source()[0];
1802 var tgt = edge.target()[0];
1803
1804 if (src === tgt) {
1805 continue;
1806 } // exclude loops
1807
1808
1809 var s = indexOf(src);
1810 var t = indexOf(tgt);
1811 var st = s * N + t; // source to target index
1812
1813 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
1814
1815
1816 if (dist[st] > _weight) {
1817 dist[st] = _weight;
1818 next[st] = t;
1819 edgeNext[st] = edge;
1820 } // If undirected graph, process 'reversed' edge
1821
1822
1823 if (!directed) {
1824 var ts = t * N + s; // target to source index
1825
1826 if (!directed && dist[ts] > _weight) {
1827 dist[ts] = _weight;
1828 next[ts] = s;
1829 edgeNext[ts] = edge;
1830 }
1831 }
1832 } // Main loop
1833
1834
1835 for (var k = 0; k < N; k++) {
1836 for (var _i2 = 0; _i2 < N; _i2++) {
1837 var ik = _i2 * N + k;
1838
1839 for (var _j = 0; _j < N; _j++) {
1840 var ij = _i2 * N + _j;
1841 var kj = k * N + _j;
1842
1843 if (dist[ik] + dist[kj] < dist[ij]) {
1844 dist[ij] = dist[ik] + dist[kj];
1845 next[ij] = next[ik];
1846 }
1847 }
1848 }
1849 }
1850
1851 var getArgEle = function getArgEle(ele) {
1852 return (string(ele) ? cy.filter(ele) : ele)[0];
1853 };
1854
1855 var indexOfArgEle = function indexOfArgEle(ele) {
1856 return indexOf(getArgEle(ele));
1857 };
1858
1859 var res = {
1860 distance: function distance(from, to) {
1861 var i = indexOfArgEle(from);
1862 var j = indexOfArgEle(to);
1863 return dist[i * N + j];
1864 },
1865 path: function path(from, to) {
1866 var i = indexOfArgEle(from);
1867 var j = indexOfArgEle(to);
1868 var fromNode = atIndex(i);
1869
1870 if (i === j) {
1871 return fromNode.collection();
1872 }
1873
1874 if (next[i * N + j] == null) {
1875 return cy.collection();
1876 }
1877
1878 var path = cy.collection();
1879 var prev = i;
1880 var edge;
1881 path.merge(fromNode);
1882
1883 while (i !== j) {
1884 prev = i;
1885 i = next[i * N + j];
1886 edge = edgeNext[prev * N + i];
1887 path.merge(edge);
1888 path.merge(atIndex(i));
1889 }
1890
1891 return path;
1892 }
1893 };
1894 return res;
1895 } // floydWarshall
1896
1897}; // elesfn
1898
1899var bellmanFordDefaults = defaults({
1900 weight: function weight(edge) {
1901 return 1;
1902 },
1903 directed: false,
1904 root: null
1905});
1906var elesfn$5 = {
1907 // Implemented from pseudocode from wikipedia
1908 bellmanFord: function bellmanFord(options) {
1909 var _this = this;
1910
1911 var _bellmanFordDefaults = bellmanFordDefaults(options),
1912 weight = _bellmanFordDefaults.weight,
1913 directed = _bellmanFordDefaults.directed,
1914 root = _bellmanFordDefaults.root;
1915
1916 var weightFn = weight;
1917 var eles = this;
1918 var cy = this.cy();
1919
1920 var _this$byGroup = this.byGroup(),
1921 edges = _this$byGroup.edges,
1922 nodes = _this$byGroup.nodes;
1923
1924 var numNodes = nodes.length;
1925 var infoMap = new Map$1();
1926 var hasNegativeWeightCycle = false;
1927 var negativeWeightCycles = [];
1928 root = cy.collection(root)[0]; // in case selector passed
1929
1930 edges.unmergeBy(function (edge) {
1931 return edge.isLoop();
1932 });
1933 var numEdges = edges.length;
1934
1935 var getInfo = function getInfo(node) {
1936 var obj = infoMap.get(node.id());
1937
1938 if (!obj) {
1939 obj = {};
1940 infoMap.set(node.id(), obj);
1941 }
1942
1943 return obj;
1944 };
1945
1946 var getNodeFromTo = function getNodeFromTo(to) {
1947 return (string(to) ? cy.$(to) : to)[0];
1948 };
1949
1950 var distanceTo = function distanceTo(to) {
1951 return getInfo(getNodeFromTo(to)).dist;
1952 };
1953
1954 var pathTo = function pathTo(to) {
1955 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
1956 var end = getNodeFromTo(to);
1957 var path = [];
1958 var node = end;
1959
1960 for (;;) {
1961 if (node == null) {
1962 return _this.spawn();
1963 }
1964
1965 var _getInfo = getInfo(node),
1966 edge = _getInfo.edge,
1967 pred = _getInfo.pred;
1968
1969 path.unshift(node[0]);
1970
1971 if (node.same(thisStart) && path.length > 0) {
1972 break;
1973 }
1974
1975 if (edge != null) {
1976 path.unshift(edge);
1977 }
1978
1979 node = pred;
1980 }
1981
1982 return eles.spawn(path);
1983 }; // Initializations { dist, pred, edge }
1984
1985
1986 for (var i = 0; i < numNodes; i++) {
1987 var node = nodes[i];
1988 var info = getInfo(node);
1989
1990 if (node.same(root)) {
1991 info.dist = 0;
1992 } else {
1993 info.dist = Infinity;
1994 }
1995
1996 info.pred = null;
1997 info.edge = null;
1998 } // Edges relaxation
1999
2000
2001 var replacedEdge = false;
2002
2003 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2004 var dist = info1.dist + weight;
2005
2006 if (dist < info2.dist && !edge.same(info1.edge)) {
2007 info2.dist = dist;
2008 info2.pred = node1;
2009 info2.edge = edge;
2010 replacedEdge = true;
2011 }
2012 };
2013
2014 for (var _i = 1; _i < numNodes; _i++) {
2015 replacedEdge = false;
2016
2017 for (var e = 0; e < numEdges; e++) {
2018 var edge = edges[e];
2019 var src = edge.source();
2020 var tgt = edge.target();
2021
2022 var _weight = weightFn(edge);
2023
2024 var srcInfo = getInfo(src);
2025 var tgtInfo = getInfo(tgt);
2026 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2027
2028 if (!directed) {
2029 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2030 }
2031 }
2032
2033 if (!replacedEdge) {
2034 break;
2035 }
2036 }
2037
2038 if (replacedEdge) {
2039 // Check for negative weight cycles
2040 for (var _e = 0; _e < numEdges; _e++) {
2041 var _edge = edges[_e];
2042
2043 var _src = _edge.source();
2044
2045 var _tgt = _edge.target();
2046
2047 var _weight2 = weightFn(_edge);
2048
2049 var srcDist = getInfo(_src).dist;
2050 var tgtDist = getInfo(_tgt).dist;
2051
2052 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2053 warn('Graph contains a negative weight cycle for Bellman-Ford');
2054 hasNegativeWeightCycle = true;
2055 break;
2056 }
2057 }
2058 }
2059
2060 return {
2061 distanceTo: distanceTo,
2062 pathTo: pathTo,
2063 hasNegativeWeightCycle: hasNegativeWeightCycle,
2064 negativeWeightCycles: negativeWeightCycles
2065 };
2066 } // bellmanFord
2067
2068}; // elesfn
2069
2070var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2071// Updates the remaining edge lists
2072// Receives as a paramater the edge which causes the collapse
2073
2074var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2075 if (remainingEdges.length === 0) {
2076 error("Karger-Stein must be run on a connected (sub)graph");
2077 }
2078
2079 var edgeInfo = remainingEdges[edgeIndex];
2080 var sourceIn = edgeInfo[1];
2081 var targetIn = edgeInfo[2];
2082 var partition1 = nodeMap[sourceIn];
2083 var partition2 = nodeMap[targetIn];
2084 var newEdges = remainingEdges; // re-use array
2085 // Delete all edges between partition1 and partition2
2086
2087 for (var i = newEdges.length - 1; i >= 0; i--) {
2088 var edge = newEdges[i];
2089 var src = edge[1];
2090 var tgt = edge[2];
2091
2092 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2093 newEdges.splice(i, 1);
2094 }
2095 } // All edges pointing to partition2 should now point to partition1
2096
2097
2098 for (var _i = 0; _i < newEdges.length; _i++) {
2099 var _edge = newEdges[_i];
2100
2101 if (_edge[1] === partition2) {
2102 // Check source
2103 newEdges[_i] = _edge.slice(); // copy
2104
2105 newEdges[_i][1] = partition1;
2106 } else if (_edge[2] === partition2) {
2107 // Check target
2108 newEdges[_i] = _edge.slice(); // copy
2109
2110 newEdges[_i][2] = partition1;
2111 }
2112 } // Move all nodes from partition2 to partition1
2113
2114
2115 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2116 if (nodeMap[_i2] === partition2) {
2117 nodeMap[_i2] = partition1;
2118 }
2119 }
2120
2121 return newEdges;
2122}; // Contracts a graph until we reach a certain number of meta nodes
2123
2124
2125var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2126 while (size > sizeLimit) {
2127 // Choose an edge randomly
2128 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2129
2130 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2131 size--;
2132 }
2133
2134 return remainingEdges;
2135};
2136
2137var elesfn$6 = {
2138 // Computes the minimum cut of an undirected graph
2139 // Returns the correct answer with high probability
2140 kargerStein: function kargerStein() {
2141 var _this = this;
2142
2143 var _this$byGroup = this.byGroup(),
2144 nodes = _this$byGroup.nodes,
2145 edges = _this$byGroup.edges;
2146
2147 edges.unmergeBy(function (edge) {
2148 return edge.isLoop();
2149 });
2150 var numNodes = nodes.length;
2151 var numEdges = edges.length;
2152 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2153 var stopSize = Math.floor(numNodes / sqrt2);
2154
2155 if (numNodes < 2) {
2156 error('At least 2 nodes are required for Karger-Stein algorithm');
2157 return undefined;
2158 } // Now store edge destination as indexes
2159 // Format for each edge (edge index, source node index, target node index)
2160
2161
2162 var edgeIndexes = [];
2163
2164 for (var i = 0; i < numEdges; i++) {
2165 var e = edges[i];
2166 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2167 } // We will store the best cut found here
2168
2169
2170 var minCutSize = Infinity;
2171 var minCutEdgeIndexes = [];
2172 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2173
2174 var metaNodeMap = new Array(numNodes);
2175 var metaNodeMap2 = new Array(numNodes);
2176
2177 var copyNodesMap = function copyNodesMap(from, to) {
2178 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2179 to[_i3] = from[_i3];
2180 }
2181 }; // Main loop
2182
2183
2184 for (var iter = 0; iter <= numIter; iter++) {
2185 // Reset meta node partition
2186 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2187 metaNodeMap[_i4] = _i4;
2188 } // Contract until stop point (stopSize nodes)
2189
2190
2191 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2192 var edgesState2 = edgesState.slice(); // copy
2193 // Create a copy of the colapsed nodes state
2194
2195 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2196
2197 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2198 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2199
2200 if (res1.length <= res2.length && res1.length < minCutSize) {
2201 minCutSize = res1.length;
2202 minCutEdgeIndexes = res1;
2203 copyNodesMap(metaNodeMap, minCutNodeMap);
2204 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2205 minCutSize = res2.length;
2206 minCutEdgeIndexes = res2;
2207 copyNodesMap(metaNodeMap2, minCutNodeMap);
2208 }
2209 } // end of main loop
2210 // Construct result
2211
2212
2213 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2214 return edges[e[0]];
2215 }));
2216 var partition1 = this.spawn();
2217 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2218
2219 var witnessNodePartition = minCutNodeMap[0];
2220
2221 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2222 var partitionId = minCutNodeMap[_i5];
2223 var node = nodes[_i5];
2224
2225 if (partitionId === witnessNodePartition) {
2226 partition1.merge(node);
2227 } else {
2228 partition2.merge(node);
2229 }
2230 } // construct components corresponding to each disjoint subset of nodes
2231
2232
2233 var constructComponent = function constructComponent(subset) {
2234 var component = _this.spawn();
2235
2236 subset.forEach(function (node) {
2237 component.merge(node);
2238 node.connectedEdges().forEach(function (edge) {
2239 // ensure edge is within calling collection and edge is not in cut
2240 if (_this.contains(edge) && !cut.contains(edge)) {
2241 component.merge(edge);
2242 }
2243 });
2244 });
2245 return component;
2246 };
2247
2248 var components = [constructComponent(partition1), constructComponent(partition2)];
2249 var ret = {
2250 cut: cut,
2251 components: components,
2252 // n.b. partitions are included to be compatible with the old api spec
2253 // (could be removed in a future major version)
2254 partition1: partition1,
2255 partition2: partition2
2256 };
2257 return ret;
2258 }
2259}; // elesfn
2260
2261var copyPosition = function copyPosition(p) {
2262 return {
2263 x: p.x,
2264 y: p.y
2265 };
2266};
2267var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
2268 return {
2269 x: p.x * zoom + pan.x,
2270 y: p.y * zoom + pan.y
2271 };
2272};
2273var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
2274 return {
2275 x: (p.x - pan.x) / zoom,
2276 y: (p.y - pan.y) / zoom
2277 };
2278};
2279var array2point = function array2point(arr) {
2280 return {
2281 x: arr[0],
2282 y: arr[1]
2283 };
2284};
2285var min = function min(arr) {
2286 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2287 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2288 var min = Infinity;
2289
2290 for (var i = begin; i < end; i++) {
2291 var val = arr[i];
2292
2293 if (isFinite(val)) {
2294 min = Math.min(val, min);
2295 }
2296 }
2297
2298 return min;
2299};
2300var max = function max(arr) {
2301 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2302 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2303 var max = -Infinity;
2304
2305 for (var i = begin; i < end; i++) {
2306 var val = arr[i];
2307
2308 if (isFinite(val)) {
2309 max = Math.max(val, max);
2310 }
2311 }
2312
2313 return max;
2314};
2315var mean = function mean(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 total = 0;
2319 var n = 0;
2320
2321 for (var i = begin; i < end; i++) {
2322 var val = arr[i];
2323
2324 if (isFinite(val)) {
2325 total += val;
2326 n++;
2327 }
2328 }
2329
2330 return total / n;
2331};
2332var median = function median(arr) {
2333 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2334 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2335 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
2336 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
2337 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
2338
2339 if (copy) {
2340 arr = arr.slice(begin, end);
2341 } else {
2342 if (end < arr.length) {
2343 arr.splice(end, arr.length - end);
2344 }
2345
2346 if (begin > 0) {
2347 arr.splice(0, begin);
2348 }
2349 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
2350
2351
2352 var off = 0; // offset from non-finite values
2353
2354 for (var i = arr.length - 1; i >= 0; i--) {
2355 var v = arr[i];
2356
2357 if (includeHoles) {
2358 if (!isFinite(v)) {
2359 arr[i] = -Infinity;
2360 off++;
2361 }
2362 } else {
2363 // just remove it if we don't want to consider holes
2364 arr.splice(i, 1);
2365 }
2366 }
2367
2368 if (sort) {
2369 arr.sort(function (a, b) {
2370 return a - b;
2371 }); // requires copy = true if you don't want to change the orig
2372 }
2373
2374 var len = arr.length;
2375 var mid = Math.floor(len / 2);
2376
2377 if (len % 2 !== 0) {
2378 return arr[mid + 1 + off];
2379 } else {
2380 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
2381 }
2382};
2383var deg2rad = function deg2rad(deg) {
2384 return Math.PI * deg / 180;
2385};
2386var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
2387 return Math.atan2(dispY, dispX) - Math.PI / 2;
2388};
2389var log2 = Math.log2 || function (n) {
2390 return Math.log(n) / Math.log(2);
2391};
2392var signum = function signum(x) {
2393 if (x > 0) {
2394 return 1;
2395 } else if (x < 0) {
2396 return -1;
2397 } else {
2398 return 0;
2399 }
2400};
2401var dist = function dist(p1, p2) {
2402 return Math.sqrt(sqdist(p1, p2));
2403};
2404var sqdist = function sqdist(p1, p2) {
2405 var dx = p2.x - p1.x;
2406 var dy = p2.y - p1.y;
2407 return dx * dx + dy * dy;
2408};
2409var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
2410 var length = v.length; // First, get sum of all elements
2411
2412 var total = 0;
2413
2414 for (var i = 0; i < length; i++) {
2415 total += v[i];
2416 } // Now, divide each by the sum of all elements
2417
2418
2419 for (var _i = 0; _i < length; _i++) {
2420 v[_i] = v[_i] / total;
2421 }
2422
2423 return v;
2424};
2425
2426var qbezierAt = function qbezierAt(p0, p1, p2, t) {
2427 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
2428};
2429var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
2430 return {
2431 x: qbezierAt(p0.x, p1.x, p2.x, t),
2432 y: qbezierAt(p0.y, p1.y, p2.y, t)
2433 };
2434};
2435var lineAt = function lineAt(p0, p1, t, d) {
2436 var vec = {
2437 x: p1.x - p0.x,
2438 y: p1.y - p0.y
2439 };
2440 var vecDist = dist(p0, p1);
2441 var normVec = {
2442 x: vec.x / vecDist,
2443 y: vec.y / vecDist
2444 };
2445 t = t == null ? 0 : t;
2446 d = d != null ? d : t * vecDist;
2447 return {
2448 x: p0.x + normVec.x * d,
2449 y: p0.y + normVec.y * d
2450 };
2451};
2452var bound = function bound(min, val, max) {
2453 return Math.max(min, Math.min(max, val));
2454}; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
2455
2456var makeBoundingBox = function makeBoundingBox(bb) {
2457 if (bb == null) {
2458 return {
2459 x1: Infinity,
2460 y1: Infinity,
2461 x2: -Infinity,
2462 y2: -Infinity,
2463 w: 0,
2464 h: 0
2465 };
2466 } else if (bb.x1 != null && bb.y1 != null) {
2467 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
2468 return {
2469 x1: bb.x1,
2470 y1: bb.y1,
2471 x2: bb.x2,
2472 y2: bb.y2,
2473 w: bb.x2 - bb.x1,
2474 h: bb.y2 - bb.y1
2475 };
2476 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
2477 return {
2478 x1: bb.x1,
2479 y1: bb.y1,
2480 x2: bb.x1 + bb.w,
2481 y2: bb.y1 + bb.h,
2482 w: bb.w,
2483 h: bb.h
2484 };
2485 }
2486 }
2487};
2488var copyBoundingBox = function copyBoundingBox(bb) {
2489 return {
2490 x1: bb.x1,
2491 x2: bb.x2,
2492 w: bb.w,
2493 y1: bb.y1,
2494 y2: bb.y2,
2495 h: bb.h
2496 };
2497};
2498var clearBoundingBox = function clearBoundingBox(bb) {
2499 bb.x1 = Infinity;
2500 bb.y1 = Infinity;
2501 bb.x2 = -Infinity;
2502 bb.y2 = -Infinity;
2503 bb.w = 0;
2504 bb.h = 0;
2505};
2506var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
2507 // update bb1 with bb2 bounds
2508 bb1.x1 = Math.min(bb1.x1, bb2.x1);
2509 bb1.x2 = Math.max(bb1.x2, bb2.x2);
2510 bb1.w = bb1.x2 - bb1.x1;
2511 bb1.y1 = Math.min(bb1.y1, bb2.y1);
2512 bb1.y2 = Math.max(bb1.y2, bb2.y2);
2513 bb1.h = bb1.y2 - bb1.y1;
2514};
2515var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
2516 bb.x1 = Math.min(bb.x1, x);
2517 bb.x2 = Math.max(bb.x2, x);
2518 bb.w = bb.x2 - bb.x1;
2519 bb.y1 = Math.min(bb.y1, y);
2520 bb.y2 = Math.max(bb.y2, y);
2521 bb.h = bb.y2 - bb.y1;
2522};
2523var expandBoundingBox = function expandBoundingBox(bb) {
2524 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2525 bb.x1 -= padding;
2526 bb.x2 += padding;
2527 bb.y1 -= padding;
2528 bb.y2 += padding;
2529 bb.w = bb.x2 - bb.x1;
2530 bb.h = bb.y2 - bb.y1;
2531 return bb;
2532};
2533var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
2534 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
2535 var top, right, bottom, left;
2536
2537 if (padding.length === 1) {
2538 top = right = bottom = left = padding[0];
2539 } else if (padding.length === 2) {
2540 top = bottom = padding[0];
2541 left = right = padding[1];
2542 } else if (padding.length === 4) {
2543 var _padding = _slicedToArray(padding, 4);
2544
2545 top = _padding[0];
2546 right = _padding[1];
2547 bottom = _padding[2];
2548 left = _padding[3];
2549 }
2550
2551 bb.x1 -= left;
2552 bb.x2 += right;
2553 bb.y1 -= top;
2554 bb.y2 += bottom;
2555 bb.w = bb.x2 - bb.x1;
2556 bb.h = bb.y2 - bb.y1;
2557 return bb;
2558};
2559
2560var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
2561 bb1.x1 = bb2.x1;
2562 bb1.y1 = bb2.y1;
2563 bb1.x2 = bb2.x2;
2564 bb1.y2 = bb2.y2;
2565 bb1.w = bb1.x2 - bb1.x1;
2566 bb1.h = bb1.y2 - bb1.y1;
2567};
2568var assignShiftToBoundingBox = function assignShiftToBoundingBox(bb, delta) {
2569 bb.x1 += delta.x;
2570 bb.x2 += delta.x;
2571 bb.y1 += delta.y;
2572 bb.y2 += delta.y;
2573};
2574var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
2575 // case: one bb to right of other
2576 if (bb1.x1 > bb2.x2) {
2577 return false;
2578 }
2579
2580 if (bb2.x1 > bb1.x2) {
2581 return false;
2582 } // case: one bb to left of other
2583
2584
2585 if (bb1.x2 < bb2.x1) {
2586 return false;
2587 }
2588
2589 if (bb2.x2 < bb1.x1) {
2590 return false;
2591 } // case: one bb above other
2592
2593
2594 if (bb1.y2 < bb2.y1) {
2595 return false;
2596 }
2597
2598 if (bb2.y2 < bb1.y1) {
2599 return false;
2600 } // case: one bb below other
2601
2602
2603 if (bb1.y1 > bb2.y2) {
2604 return false;
2605 }
2606
2607 if (bb2.y1 > bb1.y2) {
2608 return false;
2609 } // otherwise, must have some overlap
2610
2611
2612 return true;
2613};
2614var inBoundingBox = function inBoundingBox(bb, x, y) {
2615 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
2616};
2617var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
2618 return inBoundingBox(bb, pt.x, pt.y);
2619};
2620var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
2621 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
2622};
2623var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
2624 var cornerRadius = getRoundRectangleRadius(width, height);
2625 var halfWidth = width / 2;
2626 var halfHeight = height / 2; // Check intersections with straight line segments
2627
2628 var straightLineIntersections; // Top segment, left to right
2629
2630 {
2631 var topStartX = nodeX - halfWidth + cornerRadius - padding;
2632 var topStartY = nodeY - halfHeight - padding;
2633 var topEndX = nodeX + halfWidth - cornerRadius + padding;
2634 var topEndY = topStartY;
2635 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
2636
2637 if (straightLineIntersections.length > 0) {
2638 return straightLineIntersections;
2639 }
2640 } // Right segment, top to bottom
2641
2642 {
2643 var rightStartX = nodeX + halfWidth + padding;
2644 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
2645 var rightEndX = rightStartX;
2646 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
2647 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
2648
2649 if (straightLineIntersections.length > 0) {
2650 return straightLineIntersections;
2651 }
2652 } // Bottom segment, left to right
2653
2654 {
2655 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
2656 var bottomStartY = nodeY + halfHeight + padding;
2657 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
2658 var bottomEndY = bottomStartY;
2659 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
2660
2661 if (straightLineIntersections.length > 0) {
2662 return straightLineIntersections;
2663 }
2664 } // Left segment, top to bottom
2665
2666 {
2667 var leftStartX = nodeX - halfWidth - padding;
2668 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
2669 var leftEndX = leftStartX;
2670 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
2671 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
2672
2673 if (straightLineIntersections.length > 0) {
2674 return straightLineIntersections;
2675 }
2676 } // Check intersections with arc segments
2677
2678 var arcIntersections; // Top Left
2679
2680 {
2681 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
2682 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
2683 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2684
2685 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
2686 return [arcIntersections[0], arcIntersections[1]];
2687 }
2688 } // Top Right
2689
2690 {
2691 var topRightCenterX = nodeX + halfWidth - cornerRadius;
2692 var topRightCenterY = nodeY - halfHeight + cornerRadius;
2693 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2694
2695 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
2696 return [arcIntersections[0], arcIntersections[1]];
2697 }
2698 } // Bottom Right
2699
2700 {
2701 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
2702 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
2703 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2704
2705 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
2706 return [arcIntersections[0], arcIntersections[1]];
2707 }
2708 } // Bottom Left
2709
2710 {
2711 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
2712 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
2713 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2714
2715 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
2716 return [arcIntersections[0], arcIntersections[1]];
2717 }
2718 }
2719 return []; // if nothing
2720};
2721var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
2722 var t = tolerance;
2723 var x1 = Math.min(lx1, lx2);
2724 var x2 = Math.max(lx1, lx2);
2725 var y1 = Math.min(ly1, ly2);
2726 var y2 = Math.max(ly1, ly2);
2727 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
2728};
2729var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
2730 var bb = {
2731 x1: Math.min(x1, x3, x2) - tolerance,
2732 x2: Math.max(x1, x3, x2) + tolerance,
2733 y1: Math.min(y1, y3, y2) - tolerance,
2734 y2: Math.max(y1, y3, y2) + tolerance
2735 }; // if outside the rough bounding box for the bezier, then it can't be a hit
2736
2737 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
2738 // console.log('bezier out of rough bb')
2739 return false;
2740 } else {
2741 // console.log('do more expensive check');
2742 return true;
2743 }
2744};
2745var solveQuadratic = function solveQuadratic(a, b, c, val) {
2746 c -= val;
2747 var r = b * b - 4 * a * c;
2748
2749 if (r < 0) {
2750 return [];
2751 }
2752
2753 var sqrtR = Math.sqrt(r);
2754 var denom = 2 * a;
2755 var root1 = (-b + sqrtR) / denom;
2756 var root2 = (-b - sqrtR) / denom;
2757 return [root1, root2];
2758};
2759var solveCubic = function solveCubic(a, b, c, d, result) {
2760 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
2761 // r is the real component, i is the imaginary component
2762 // An implementation of the Cardano method from the year 1545
2763 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
2764 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
2765
2766 if (a === 0) {
2767 a = epsilon;
2768 }
2769
2770 b /= a;
2771 c /= a;
2772 d /= a;
2773 var discriminant, q, r, dum1, s, t, term1, r13;
2774 q = (3.0 * c - b * b) / 9.0;
2775 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
2776 r /= 54.0;
2777 discriminant = q * q * q + r * r;
2778 result[1] = 0;
2779 term1 = b / 3.0;
2780
2781 if (discriminant > 0) {
2782 s = r + Math.sqrt(discriminant);
2783 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
2784 t = r - Math.sqrt(discriminant);
2785 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
2786 result[0] = -term1 + s + t;
2787 term1 += (s + t) / 2.0;
2788 result[4] = result[2] = -term1;
2789 term1 = Math.sqrt(3.0) * (-t + s) / 2;
2790 result[3] = term1;
2791 result[5] = -term1;
2792 return;
2793 }
2794
2795 result[5] = result[3] = 0;
2796
2797 if (discriminant === 0) {
2798 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
2799 result[0] = -term1 + 2.0 * r13;
2800 result[4] = result[2] = -(r13 + term1);
2801 return;
2802 }
2803
2804 q = -q;
2805 dum1 = q * q * q;
2806 dum1 = Math.acos(r / Math.sqrt(dum1));
2807 r13 = 2.0 * Math.sqrt(q);
2808 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
2809 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
2810 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
2811 return;
2812};
2813var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
2814 // Find minimum distance by using the minimum of the distance
2815 // function between the given point and the curve
2816 // This gives the coefficients of the resulting cubic equation
2817 // whose roots tell us where a possible minimum is
2818 // (Coefficients are divided by 4)
2819 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;
2820 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;
2821 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;
2822 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);
2823
2824 var roots = []; // Use the cubic solving algorithm
2825
2826 solveCubic(a, b, c, d, roots);
2827 var zeroThreshold = 0.0000001;
2828 var params = [];
2829
2830 for (var index = 0; index < 6; index += 2) {
2831 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
2832 params.push(roots[index]);
2833 }
2834 }
2835
2836 params.push(1.0);
2837 params.push(0.0);
2838 var minDistanceSquared = -1;
2839 var curX, curY, distSquared;
2840
2841 for (var i = 0; i < params.length; i++) {
2842 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
2843 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
2844 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
2845
2846 if (minDistanceSquared >= 0) {
2847 if (distSquared < minDistanceSquared) {
2848 minDistanceSquared = distSquared;
2849 }
2850 } else {
2851 minDistanceSquared = distSquared;
2852 }
2853 }
2854
2855 return minDistanceSquared;
2856};
2857var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
2858 var offset = [x - x1, y - y1];
2859 var line = [x2 - x1, y2 - y1];
2860 var lineSq = line[0] * line[0] + line[1] * line[1];
2861 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
2862 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
2863 var adjSq = dotProduct * dotProduct / lineSq;
2864
2865 if (dotProduct < 0) {
2866 return hypSq;
2867 }
2868
2869 if (adjSq > lineSq) {
2870 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
2871 }
2872
2873 return hypSq - adjSq;
2874};
2875var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
2876 var x1, y1, x2, y2;
2877 var y3; // Intersect with vertical line through (x, y)
2878
2879 var up = 0; // let down = 0;
2880
2881 for (var i = 0; i < points.length / 2; i++) {
2882 x1 = points[i * 2];
2883 y1 = points[i * 2 + 1];
2884
2885 if (i + 1 < points.length / 2) {
2886 x2 = points[(i + 1) * 2];
2887 y2 = points[(i + 1) * 2 + 1];
2888 } else {
2889 x2 = points[(i + 1 - points.length / 2) * 2];
2890 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
2891 }
2892
2893 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
2894 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
2895
2896 if (y3 > y) {
2897 up++;
2898 } // if( y3 < y ){
2899 // down++;
2900 // }
2901
2902 } else {
2903 continue;
2904 }
2905 }
2906
2907 if (up % 2 === 0) {
2908 return false;
2909 } else {
2910 return true;
2911 }
2912};
2913var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
2914 var transformedPoints = new Array(basePoints.length); // Gives negative angle
2915
2916 var angle;
2917
2918 if (direction[0] != null) {
2919 angle = Math.atan(direction[1] / direction[0]);
2920
2921 if (direction[0] < 0) {
2922 angle = angle + Math.PI / 2;
2923 } else {
2924 angle = -angle - Math.PI / 2;
2925 }
2926 } else {
2927 angle = direction;
2928 }
2929
2930 var cos = Math.cos(-angle);
2931 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
2932
2933 for (var i = 0; i < transformedPoints.length / 2; i++) {
2934 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
2935 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
2936 transformedPoints[i * 2] += centerX;
2937 transformedPoints[i * 2 + 1] += centerY;
2938 }
2939
2940 var points;
2941
2942 if (padding > 0) {
2943 var expandedLineSet = expandPolygon(transformedPoints, -padding);
2944 points = joinLines(expandedLineSet);
2945 } else {
2946 points = transformedPoints;
2947 }
2948
2949 return pointInsidePolygonPoints(x, y, points);
2950};
2951var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
2952 var cutPolygonPoints = new Array(basePoints.length);
2953 var halfW = width / 2;
2954 var halfH = height / 2;
2955 var cornerRadius = getRoundPolygonRadius(width, height);
2956 var squaredCornerRadius = cornerRadius * cornerRadius;
2957
2958 for (var i = 0; i < basePoints.length / 4; i++) {
2959 var sourceUv = void 0,
2960 destUv = void 0;
2961
2962 if (i === 0) {
2963 sourceUv = basePoints.length - 2;
2964 } else {
2965 sourceUv = i * 4 - 2;
2966 }
2967
2968 destUv = i * 4 + 2;
2969 var px = centerX + halfW * basePoints[i * 4];
2970 var py = centerY + halfH * basePoints[i * 4 + 1];
2971 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
2972 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
2973 var cp0x = px - offset * basePoints[sourceUv];
2974 var cp0y = py - offset * basePoints[sourceUv + 1];
2975 var cp1x = px + offset * basePoints[destUv];
2976 var cp1y = py + offset * basePoints[destUv + 1];
2977 cutPolygonPoints[i * 4] = cp0x;
2978 cutPolygonPoints[i * 4 + 1] = cp0y;
2979 cutPolygonPoints[i * 4 + 2] = cp1x;
2980 cutPolygonPoints[i * 4 + 3] = cp1y;
2981 var orthx = basePoints[sourceUv + 1];
2982 var orthy = -basePoints[sourceUv];
2983 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
2984
2985 if (cosAlpha < 0) {
2986 orthx *= -1;
2987 orthy *= -1;
2988 }
2989
2990 var cx = cp0x + orthx * cornerRadius;
2991 var cy = cp0y + orthy * cornerRadius;
2992 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
2993
2994 if (squaredDistance <= squaredCornerRadius) {
2995 return true;
2996 }
2997 }
2998
2999 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
3000};
3001var joinLines = function joinLines(lineSet) {
3002 var vertices = new Array(lineSet.length / 2);
3003 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3004 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3005
3006 for (var i = 0; i < lineSet.length / 4; i++) {
3007 currentLineStartX = lineSet[i * 4];
3008 currentLineStartY = lineSet[i * 4 + 1];
3009 currentLineEndX = lineSet[i * 4 + 2];
3010 currentLineEndY = lineSet[i * 4 + 3];
3011
3012 if (i < lineSet.length / 4 - 1) {
3013 nextLineStartX = lineSet[(i + 1) * 4];
3014 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3015 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3016 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3017 } else {
3018 nextLineStartX = lineSet[0];
3019 nextLineStartY = lineSet[1];
3020 nextLineEndX = lineSet[2];
3021 nextLineEndY = lineSet[3];
3022 }
3023
3024 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3025 vertices[i * 2] = intersection[0];
3026 vertices[i * 2 + 1] = intersection[1];
3027 }
3028
3029 return vertices;
3030};
3031var expandPolygon = function expandPolygon(points, pad) {
3032 var expandedLineSet = new Array(points.length * 2);
3033 var currentPointX, currentPointY, nextPointX, nextPointY;
3034
3035 for (var i = 0; i < points.length / 2; i++) {
3036 currentPointX = points[i * 2];
3037 currentPointY = points[i * 2 + 1];
3038
3039 if (i < points.length / 2 - 1) {
3040 nextPointX = points[(i + 1) * 2];
3041 nextPointY = points[(i + 1) * 2 + 1];
3042 } else {
3043 nextPointX = points[0];
3044 nextPointY = points[1];
3045 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3046 // Assume CCW polygon winding
3047
3048
3049 var offsetX = nextPointY - currentPointY;
3050 var offsetY = -(nextPointX - currentPointX); // Normalize
3051
3052 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3053 var normalizedOffsetX = offsetX / offsetLength;
3054 var normalizedOffsetY = offsetY / offsetLength;
3055 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3056 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3057 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3058 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3059 }
3060
3061 return expandedLineSet;
3062};
3063var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3064 var dispX = centerX - x;
3065 var dispY = centerY - y;
3066 dispX /= ellipseWradius;
3067 dispY /= ellipseHradius;
3068 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3069 var newLength = len - 1;
3070
3071 if (newLength < 0) {
3072 return [];
3073 }
3074
3075 var lenProportion = newLength / len;
3076 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3077};
3078var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3079 x -= centerX;
3080 y -= centerY;
3081 x /= width / 2 + padding;
3082 y /= height / 2 + padding;
3083 return x * x + y * y <= 1;
3084}; // Returns intersections of increasing distance from line's start point
3085
3086var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3087 // Calculate d, direction vector of line
3088 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3089
3090 var f = [x1 - centerX, y1 - centerY];
3091 var a = d[0] * d[0] + d[1] * d[1];
3092 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3093 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3094 var discriminant = b * b - 4 * a * c;
3095
3096 if (discriminant < 0) {
3097 return [];
3098 }
3099
3100 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3101 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3102 var tMin = Math.min(t1, t2);
3103 var tMax = Math.max(t1, t2);
3104 var inRangeParams = [];
3105
3106 if (tMin >= 0 && tMin <= 1) {
3107 inRangeParams.push(tMin);
3108 }
3109
3110 if (tMax >= 0 && tMax <= 1) {
3111 inRangeParams.push(tMax);
3112 }
3113
3114 if (inRangeParams.length === 0) {
3115 return [];
3116 }
3117
3118 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3119 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3120
3121 if (inRangeParams.length > 1) {
3122 if (inRangeParams[0] == inRangeParams[1]) {
3123 return [nearIntersectionX, nearIntersectionY];
3124 } else {
3125 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3126 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3127 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3128 }
3129 } else {
3130 return [nearIntersectionX, nearIntersectionY];
3131 }
3132};
3133var midOfThree = function midOfThree(a, b, c) {
3134 if (b <= a && a <= c || c <= a && a <= b) {
3135 return a;
3136 } else if (a <= b && b <= c || c <= b && b <= a) {
3137 return b;
3138 } else {
3139 return c;
3140 }
3141}; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3142
3143var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3144 var dx13 = x1 - x3;
3145 var dx21 = x2 - x1;
3146 var dx43 = x4 - x3;
3147 var dy13 = y1 - y3;
3148 var dy21 = y2 - y1;
3149 var dy43 = y4 - y3;
3150 var ua_t = dx43 * dy13 - dy43 * dx13;
3151 var ub_t = dx21 * dy13 - dy21 * dx13;
3152 var u_b = dy43 * dx21 - dx43 * dy21;
3153
3154 if (u_b !== 0) {
3155 var ua = ua_t / u_b;
3156 var ub = ub_t / u_b;
3157 var flptThreshold = 0.001;
3158
3159 var _min = 0 - flptThreshold;
3160
3161 var _max = 1 + flptThreshold;
3162
3163 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3164 return [x1 + ua * dx21, y1 + ua * dy21];
3165 } else {
3166 if (!infiniteLines) {
3167 return [];
3168 } else {
3169 return [x1 + ua * dx21, y1 + ua * dy21];
3170 }
3171 }
3172 } else {
3173 if (ua_t === 0 || ub_t === 0) {
3174 // Parallel, coincident lines. Check if overlap
3175 // Check endpoint of second line
3176 if (midOfThree(x1, x2, x4) === x4) {
3177 return [x4, y4];
3178 } // Check start point of second line
3179
3180
3181 if (midOfThree(x1, x2, x3) === x3) {
3182 return [x3, y3];
3183 } // Endpoint of first line
3184
3185
3186 if (midOfThree(x3, x4, x2) === x2) {
3187 return [x2, y2];
3188 }
3189
3190 return [];
3191 } else {
3192 // Parallel, non-coincident
3193 return [];
3194 }
3195 }
3196}; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3197// intersect a node polygon (pts transformed)
3198//
3199// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3200// intersect the points (no transform)
3201
3202var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3203 var intersections = [];
3204 var intersection;
3205 var transformedPoints = new Array(basePoints.length);
3206 var doTransform = true;
3207
3208 if (width == null) {
3209 doTransform = false;
3210 }
3211
3212 var points;
3213
3214 if (doTransform) {
3215 for (var i = 0; i < transformedPoints.length / 2; i++) {
3216 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3217 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3218 }
3219
3220 if (padding > 0) {
3221 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3222 points = joinLines(expandedLineSet);
3223 } else {
3224 points = transformedPoints;
3225 }
3226 } else {
3227 points = basePoints;
3228 }
3229
3230 var currentX, currentY, nextX, nextY;
3231
3232 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3233 currentX = points[_i2 * 2];
3234 currentY = points[_i2 * 2 + 1];
3235
3236 if (_i2 < points.length / 2 - 1) {
3237 nextX = points[(_i2 + 1) * 2];
3238 nextY = points[(_i2 + 1) * 2 + 1];
3239 } else {
3240 nextX = points[0];
3241 nextY = points[1];
3242 }
3243
3244 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3245
3246 if (intersection.length !== 0) {
3247 intersections.push(intersection[0], intersection[1]);
3248 }
3249 }
3250
3251 return intersections;
3252};
3253var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3254 var intersections = [];
3255 var intersection;
3256 var lines = new Array(basePoints.length);
3257 var halfW = width / 2;
3258 var halfH = height / 2;
3259 var cornerRadius = getRoundPolygonRadius(width, height);
3260
3261 for (var i = 0; i < basePoints.length / 4; i++) {
3262 var sourceUv = void 0,
3263 destUv = void 0;
3264
3265 if (i === 0) {
3266 sourceUv = basePoints.length - 2;
3267 } else {
3268 sourceUv = i * 4 - 2;
3269 }
3270
3271 destUv = i * 4 + 2;
3272 var px = centerX + halfW * basePoints[i * 4];
3273 var py = centerY + halfH * basePoints[i * 4 + 1];
3274 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3275 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3276 var cp0x = px - offset * basePoints[sourceUv];
3277 var cp0y = py - offset * basePoints[sourceUv + 1];
3278 var cp1x = px + offset * basePoints[destUv];
3279 var cp1y = py + offset * basePoints[destUv + 1];
3280
3281 if (i === 0) {
3282 lines[basePoints.length - 2] = cp0x;
3283 lines[basePoints.length - 1] = cp0y;
3284 } else {
3285 lines[i * 4 - 2] = cp0x;
3286 lines[i * 4 - 1] = cp0y;
3287 }
3288
3289 lines[i * 4] = cp1x;
3290 lines[i * 4 + 1] = cp1y;
3291 var orthx = basePoints[sourceUv + 1];
3292 var orthy = -basePoints[sourceUv];
3293 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3294
3295 if (cosAlpha < 0) {
3296 orthx *= -1;
3297 orthy *= -1;
3298 }
3299
3300 var cx = cp0x + orthx * cornerRadius;
3301 var cy = cp0y + orthy * cornerRadius;
3302 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
3303
3304 if (intersection.length !== 0) {
3305 intersections.push(intersection[0], intersection[1]);
3306 }
3307 }
3308
3309 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
3310 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
3311
3312 if (intersection.length !== 0) {
3313 intersections.push(intersection[0], intersection[1]);
3314 }
3315 }
3316
3317 if (intersections.length > 2) {
3318 var lowestIntersection = [intersections[0], intersections[1]];
3319 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3320
3321 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
3322 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
3323
3324 if (squaredDistance <= lowestSquaredDistance) {
3325 lowestIntersection[0] = intersections[_i4 * 2];
3326 lowestIntersection[1] = intersections[_i4 * 2 + 1];
3327 lowestSquaredDistance = squaredDistance;
3328 }
3329 }
3330
3331 return lowestIntersection;
3332 }
3333
3334 return intersections;
3335};
3336var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3337 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3338 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3339 var lenRatio = (length - amount) / length;
3340
3341 if (lenRatio < 0) {
3342 lenRatio = 0.00001;
3343 }
3344
3345 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3346};
3347var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3348 var points = generateUnitNgonPoints(sides, rotationRadians);
3349 points = fitPolygonToSquare(points);
3350 return points;
3351};
3352var fitPolygonToSquare = function fitPolygonToSquare(points) {
3353 var x, y;
3354 var sides = points.length / 2;
3355 var minX = Infinity,
3356 minY = Infinity,
3357 maxX = -Infinity,
3358 maxY = -Infinity;
3359
3360 for (var i = 0; i < sides; i++) {
3361 x = points[2 * i];
3362 y = points[2 * i + 1];
3363 minX = Math.min(minX, x);
3364 maxX = Math.max(maxX, x);
3365 minY = Math.min(minY, y);
3366 maxY = Math.max(maxY, y);
3367 } // stretch factors
3368
3369
3370 var sx = 2 / (maxX - minX);
3371 var sy = 2 / (maxY - minY);
3372
3373 for (var _i5 = 0; _i5 < sides; _i5++) {
3374 x = points[2 * _i5] = points[2 * _i5] * sx;
3375 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
3376 minX = Math.min(minX, x);
3377 maxX = Math.max(maxX, x);
3378 minY = Math.min(minY, y);
3379 maxY = Math.max(maxY, y);
3380 }
3381
3382 if (minY < -1) {
3383 for (var _i6 = 0; _i6 < sides; _i6++) {
3384 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
3385 }
3386 }
3387
3388 return points;
3389};
3390var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
3391 var increment = 1.0 / sides * 2 * Math.PI;
3392 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
3393 startAngle += rotationRadians;
3394 var points = new Array(sides * 2);
3395 var currentAngle;
3396
3397 for (var i = 0; i < sides; i++) {
3398 currentAngle = i * increment + startAngle;
3399 points[2 * i] = Math.cos(currentAngle); // x
3400
3401 points[2 * i + 1] = Math.sin(-currentAngle); // y
3402 }
3403
3404 return points;
3405}; // Set the default radius, unless half of width or height is smaller than default
3406
3407var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
3408 return Math.min(width / 4, height / 4, 8);
3409}; // Set the default radius
3410
3411var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
3412 return Math.min(width / 10, height / 10, 8);
3413};
3414var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
3415 return 8;
3416};
3417var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
3418 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
3419}; // get curve width, height, and control point position offsets as a percentage of node height / width
3420
3421var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
3422 return {
3423 heightOffset: Math.min(15, 0.05 * height),
3424 widthOffset: Math.min(100, 0.25 * width),
3425 ctrlPtOffsetPct: 0.05
3426 };
3427};
3428
3429var pageRankDefaults = defaults({
3430 dampingFactor: 0.8,
3431 precision: 0.000001,
3432 iterations: 200,
3433 weight: function weight(edge) {
3434 return 1;
3435 }
3436});
3437var elesfn$7 = {
3438 pageRank: function pageRank(options) {
3439 var _pageRankDefaults = pageRankDefaults(options),
3440 dampingFactor = _pageRankDefaults.dampingFactor,
3441 precision = _pageRankDefaults.precision,
3442 iterations = _pageRankDefaults.iterations,
3443 weight = _pageRankDefaults.weight;
3444
3445 var cy = this._private.cy;
3446
3447 var _this$byGroup = this.byGroup(),
3448 nodes = _this$byGroup.nodes,
3449 edges = _this$byGroup.edges;
3450
3451 var numNodes = nodes.length;
3452 var numNodesSqd = numNodes * numNodes;
3453 var numEdges = edges.length; // Construct transposed adjacency matrix
3454 // First lets have a zeroed matrix of the right size
3455 // We'll also keep track of the sum of each column
3456
3457 var matrix = new Array(numNodesSqd);
3458 var columnSum = new Array(numNodes);
3459 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
3460
3461 for (var i = 0; i < numNodes; i++) {
3462 for (var j = 0; j < numNodes; j++) {
3463 var n = i * numNodes + j;
3464 matrix[n] = 0;
3465 }
3466
3467 columnSum[i] = 0;
3468 } // Now, process edges
3469
3470
3471 for (var _i = 0; _i < numEdges; _i++) {
3472 var edge = edges[_i];
3473 var srcId = edge.data('source');
3474 var tgtId = edge.data('target'); // Don't include loops in the matrix
3475
3476 if (srcId === tgtId) {
3477 continue;
3478 }
3479
3480 var s = nodes.indexOfId(srcId);
3481 var t = nodes.indexOfId(tgtId);
3482 var w = weight(edge);
3483
3484 var _n = t * numNodes + s; // Update matrix
3485
3486
3487 matrix[_n] += w; // Update column sum
3488
3489 columnSum[s] += w;
3490 } // Add additional probability based on damping factor
3491 // Also, take into account columns that have sum = 0
3492
3493
3494 var p = 1.0 / numNodes + additionalProb; // Shorthand
3495 // Traverse matrix, column by column
3496
3497 for (var _j = 0; _j < numNodes; _j++) {
3498 if (columnSum[_j] === 0) {
3499 // No 'links' out from node jth, assume equal probability for each possible node
3500 for (var _i2 = 0; _i2 < numNodes; _i2++) {
3501 var _n2 = _i2 * numNodes + _j;
3502
3503 matrix[_n2] = p;
3504 }
3505 } else {
3506 // Node jth has outgoing link, compute normalized probabilities
3507 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3508 var _n3 = _i3 * numNodes + _j;
3509
3510 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
3511 }
3512 }
3513 } // Compute dominant eigenvector using power method
3514
3515
3516 var eigenvector = new Array(numNodes);
3517 var temp = new Array(numNodes);
3518 var previous; // Start with a vector of all 1's
3519 // Also, initialize a null vector which will be used as shorthand
3520
3521 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3522 eigenvector[_i4] = 1;
3523 }
3524
3525 for (var iter = 0; iter < iterations; iter++) {
3526 // Temp array with all 0's
3527 for (var _i5 = 0; _i5 < numNodes; _i5++) {
3528 temp[_i5] = 0;
3529 } // Multiply matrix with previous result
3530
3531
3532 for (var _i6 = 0; _i6 < numNodes; _i6++) {
3533 for (var _j2 = 0; _j2 < numNodes; _j2++) {
3534 var _n4 = _i6 * numNodes + _j2;
3535
3536 temp[_i6] += matrix[_n4] * eigenvector[_j2];
3537 }
3538 }
3539
3540 inPlaceSumNormalize(temp);
3541 previous = eigenvector;
3542 eigenvector = temp;
3543 temp = previous;
3544 var diff = 0; // Compute difference (squared module) of both vectors
3545
3546 for (var _i7 = 0; _i7 < numNodes; _i7++) {
3547 var delta = previous[_i7] - eigenvector[_i7];
3548 diff += delta * delta;
3549 } // If difference is less than the desired threshold, stop iterating
3550
3551
3552 if (diff < precision) {
3553 break;
3554 }
3555 } // Construct result
3556
3557
3558 var res = {
3559 rank: function rank(node) {
3560 node = cy.collection(node)[0];
3561 return eigenvector[nodes.indexOf(node)];
3562 }
3563 };
3564 return res;
3565 } // pageRank
3566
3567}; // elesfn
3568
3569var defaults$1 = defaults({
3570 root: null,
3571 weight: function weight(edge) {
3572 return 1;
3573 },
3574 directed: false,
3575 alpha: 0
3576});
3577var elesfn$8 = {
3578 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
3579 options = defaults$1(options);
3580 var cy = this.cy();
3581 var nodes = this.nodes();
3582 var numNodes = nodes.length;
3583
3584 if (!options.directed) {
3585 var degrees = {};
3586 var maxDegree = 0;
3587
3588 for (var i = 0; i < numNodes; i++) {
3589 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
3590
3591 options.root = node;
3592 var currDegree = this.degreeCentrality(options);
3593
3594 if (maxDegree < currDegree.degree) {
3595 maxDegree = currDegree.degree;
3596 }
3597
3598 degrees[node.id()] = currDegree.degree;
3599 }
3600
3601 return {
3602 degree: function degree(node) {
3603 if (maxDegree === 0) {
3604 return 0;
3605 }
3606
3607 if (string(node)) {
3608 // from is a selector string
3609 node = cy.filter(node);
3610 }
3611
3612 return degrees[node.id()] / maxDegree;
3613 }
3614 };
3615 } else {
3616 var indegrees = {};
3617 var outdegrees = {};
3618 var maxIndegree = 0;
3619 var maxOutdegree = 0;
3620
3621 for (var _i = 0; _i < numNodes; _i++) {
3622 var _node = nodes[_i];
3623
3624 var id = _node.id(); // add current node to the current options object and call degreeCentrality
3625
3626
3627 options.root = _node;
3628
3629 var _currDegree = this.degreeCentrality(options);
3630
3631 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
3632 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
3633 indegrees[id] = _currDegree.indegree;
3634 outdegrees[id] = _currDegree.outdegree;
3635 }
3636
3637 return {
3638 indegree: function indegree(node) {
3639 if (maxIndegree == 0) {
3640 return 0;
3641 }
3642
3643 if (string(node)) {
3644 // from is a selector string
3645 node = cy.filter(node);
3646 }
3647
3648 return indegrees[node.id()] / maxIndegree;
3649 },
3650 outdegree: function outdegree(node) {
3651 if (maxOutdegree === 0) {
3652 return 0;
3653 }
3654
3655 if (string(node)) {
3656 // from is a selector string
3657 node = cy.filter(node);
3658 }
3659
3660 return outdegrees[node.id()] / maxOutdegree;
3661 }
3662 };
3663 }
3664 },
3665 // degreeCentralityNormalized
3666 // Implemented from the algorithm in Opsahl's paper
3667 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
3668 // check the heading 2 "Degree"
3669 degreeCentrality: function degreeCentrality(options) {
3670 options = defaults$1(options);
3671 var cy = this.cy();
3672 var callingEles = this;
3673 var _options = options,
3674 root = _options.root,
3675 weight = _options.weight,
3676 directed = _options.directed,
3677 alpha = _options.alpha;
3678 root = cy.collection(root)[0];
3679
3680 if (!directed) {
3681 var connEdges = root.connectedEdges().intersection(callingEles);
3682 var k = connEdges.length;
3683 var s = 0; // Now, sum edge weights
3684
3685 for (var i = 0; i < connEdges.length; i++) {
3686 s += weight(connEdges[i]);
3687 }
3688
3689 return {
3690 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
3691 };
3692 } else {
3693 var edges = root.connectedEdges();
3694 var incoming = edges.filter(function (edge) {
3695 return edge.target().same(root) && callingEles.has(edge);
3696 });
3697 var outgoing = edges.filter(function (edge) {
3698 return edge.source().same(root) && callingEles.has(edge);
3699 });
3700 var k_in = incoming.length;
3701 var k_out = outgoing.length;
3702 var s_in = 0;
3703 var s_out = 0; // Now, sum incoming edge weights
3704
3705 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
3706 s_in += weight(incoming[_i2]);
3707 } // Now, sum outgoing edge weights
3708
3709
3710 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
3711 s_out += weight(outgoing[_i3]);
3712 }
3713
3714 return {
3715 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
3716 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
3717 };
3718 }
3719 } // degreeCentrality
3720
3721}; // elesfn
3722// nice, short mathemathical alias
3723
3724elesfn$8.dc = elesfn$8.degreeCentrality;
3725elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
3726
3727var defaults$2 = defaults({
3728 harmonic: true,
3729 weight: function weight() {
3730 return 1;
3731 },
3732 directed: false,
3733 root: null
3734});
3735var elesfn$9 = {
3736 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
3737 var _defaults = defaults$2(options),
3738 harmonic = _defaults.harmonic,
3739 weight = _defaults.weight,
3740 directed = _defaults.directed;
3741
3742 var cy = this.cy();
3743 var closenesses = {};
3744 var maxCloseness = 0;
3745 var nodes = this.nodes();
3746 var fw = this.floydWarshall({
3747 weight: weight,
3748 directed: directed
3749 }); // Compute closeness for every node and find the maximum closeness
3750
3751 for (var i = 0; i < nodes.length; i++) {
3752 var currCloseness = 0;
3753 var node_i = nodes[i];
3754
3755 for (var j = 0; j < nodes.length; j++) {
3756 if (i !== j) {
3757 var d = fw.distance(node_i, nodes[j]);
3758
3759 if (harmonic) {
3760 currCloseness += 1 / d;
3761 } else {
3762 currCloseness += d;
3763 }
3764 }
3765 }
3766
3767 if (!harmonic) {
3768 currCloseness = 1 / currCloseness;
3769 }
3770
3771 if (maxCloseness < currCloseness) {
3772 maxCloseness = currCloseness;
3773 }
3774
3775 closenesses[node_i.id()] = currCloseness;
3776 }
3777
3778 return {
3779 closeness: function closeness(node) {
3780 if (maxCloseness == 0) {
3781 return 0;
3782 }
3783
3784 if (string(node)) {
3785 // from is a selector string
3786 node = cy.filter(node)[0].id();
3787 } else {
3788 // from is a node
3789 node = node.id();
3790 }
3791
3792 return closenesses[node] / maxCloseness;
3793 }
3794 };
3795 },
3796 // Implemented from pseudocode from wikipedia
3797 closenessCentrality: function closenessCentrality(options) {
3798 var _defaults2 = defaults$2(options),
3799 root = _defaults2.root,
3800 weight = _defaults2.weight,
3801 directed = _defaults2.directed,
3802 harmonic = _defaults2.harmonic;
3803
3804 root = this.filter(root)[0]; // we need distance from this node to every other node
3805
3806 var dijkstra = this.dijkstra({
3807 root: root,
3808 weight: weight,
3809 directed: directed
3810 });
3811 var totalDistance = 0;
3812 var nodes = this.nodes();
3813
3814 for (var i = 0; i < nodes.length; i++) {
3815 var n = nodes[i];
3816
3817 if (!n.same(root)) {
3818 var d = dijkstra.distanceTo(n);
3819
3820 if (harmonic) {
3821 totalDistance += 1 / d;
3822 } else {
3823 totalDistance += d;
3824 }
3825 }
3826 }
3827
3828 return harmonic ? totalDistance : 1 / totalDistance;
3829 } // closenessCentrality
3830
3831}; // elesfn
3832// nice, short mathemathical alias
3833
3834elesfn$9.cc = elesfn$9.closenessCentrality;
3835elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
3836
3837var defaults$3 = defaults({
3838 weight: null,
3839 directed: false
3840});
3841var elesfn$a = {
3842 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
3843 betweennessCentrality: function betweennessCentrality(options) {
3844 var _defaults = defaults$3(options),
3845 directed = _defaults.directed,
3846 weight = _defaults.weight;
3847
3848 var weighted = weight != null;
3849 var cy = this.cy(); // starting
3850
3851 var V = this.nodes();
3852 var A = {};
3853 var _C = {};
3854 var max = 0;
3855 var C = {
3856 set: function set(key, val) {
3857 _C[key] = val;
3858
3859 if (val > max) {
3860 max = val;
3861 }
3862 },
3863 get: function get(key) {
3864 return _C[key];
3865 }
3866 }; // A contains the neighborhoods of every node
3867
3868 for (var i = 0; i < V.length; i++) {
3869 var v = V[i];
3870 var vid = v.id();
3871
3872 if (directed) {
3873 A[vid] = v.outgoers().nodes(); // get outgoers of every node
3874 } else {
3875 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
3876 }
3877
3878 C.set(vid, 0);
3879 }
3880
3881 var _loop = function _loop(s) {
3882 var sid = V[s].id();
3883 var S = []; // stack
3884
3885 var P = {};
3886 var g = {};
3887 var d = {};
3888 var Q = new Heap(function (a, b) {
3889 return d[a] - d[b];
3890 }); // queue
3891 // init dictionaries
3892
3893 for (var _i = 0; _i < V.length; _i++) {
3894 var _vid = V[_i].id();
3895
3896 P[_vid] = [];
3897 g[_vid] = 0;
3898 d[_vid] = Infinity;
3899 }
3900
3901 g[sid] = 1; // sigma
3902
3903 d[sid] = 0; // distance to s
3904
3905 Q.push(sid);
3906
3907 while (!Q.empty()) {
3908 var _v = Q.pop();
3909
3910 S.push(_v);
3911
3912 if (weighted) {
3913 for (var j = 0; j < A[_v].length; j++) {
3914 var w = A[_v][j];
3915 var vEle = cy.getElementById(_v);
3916 var edge = void 0;
3917
3918 if (vEle.edgesTo(w).length > 0) {
3919 edge = vEle.edgesTo(w)[0];
3920 } else {
3921 edge = w.edgesTo(vEle)[0];
3922 }
3923
3924 var edgeWeight = weight(edge);
3925 w = w.id();
3926
3927 if (d[w] > d[_v] + edgeWeight) {
3928 d[w] = d[_v] + edgeWeight;
3929
3930 if (Q.nodes.indexOf(w) < 0) {
3931 //if w is not in Q
3932 Q.push(w);
3933 } else {
3934 // update position if w is in Q
3935 Q.updateItem(w);
3936 }
3937
3938 g[w] = 0;
3939 P[w] = [];
3940 }
3941
3942 if (d[w] == d[_v] + edgeWeight) {
3943 g[w] = g[w] + g[_v];
3944 P[w].push(_v);
3945 }
3946 }
3947 } else {
3948 for (var _j = 0; _j < A[_v].length; _j++) {
3949 var _w = A[_v][_j].id();
3950
3951 if (d[_w] == Infinity) {
3952 Q.push(_w);
3953 d[_w] = d[_v] + 1;
3954 }
3955
3956 if (d[_w] == d[_v] + 1) {
3957 g[_w] = g[_w] + g[_v];
3958
3959 P[_w].push(_v);
3960 }
3961 }
3962 }
3963 }
3964
3965 var e = {};
3966
3967 for (var _i2 = 0; _i2 < V.length; _i2++) {
3968 e[V[_i2].id()] = 0;
3969 }
3970
3971 while (S.length > 0) {
3972 var _w2 = S.pop();
3973
3974 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
3975 var _v2 = P[_w2][_j2];
3976 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
3977 }
3978
3979 if (_w2 != V[s].id()) {
3980 C.set(_w2, C.get(_w2) + e[_w2]);
3981 }
3982 }
3983 };
3984
3985 for (var s = 0; s < V.length; s++) {
3986 _loop(s);
3987 }
3988
3989 var ret = {
3990 betweenness: function betweenness(node) {
3991 var id = cy.collection(node).id();
3992 return C.get(id);
3993 },
3994 betweennessNormalized: function betweennessNormalized(node) {
3995 if (max == 0) {
3996 return 0;
3997 }
3998
3999 var id = cy.collection(node).id();
4000 return C.get(id) / max;
4001 }
4002 }; // alias
4003
4004 ret.betweennessNormalised = ret.betweennessNormalized;
4005 return ret;
4006 } // betweennessCentrality
4007
4008}; // elesfn
4009// nice, short mathemathical alias
4010
4011elesfn$a.bc = elesfn$a.betweennessCentrality;
4012
4013// Implemented by Zoe Xi @zoexi for GSOC 2016
4014/* eslint-disable no-unused-vars */
4015
4016var defaults$4 = defaults({
4017 expandFactor: 2,
4018 // affects time of computation and cluster granularity to some extent: M * M
4019 inflateFactor: 2,
4020 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4021 multFactor: 1,
4022 // optional self loops for each node. Use a neutral value to improve cluster computations.
4023 maxIterations: 20,
4024 // maximum number of iterations of the MCL algorithm in a single run
4025 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4026 function (edge) {
4027 return 1;
4028 }]
4029});
4030/* eslint-enable */
4031
4032var setOptions = function setOptions(options) {
4033 return defaults$4(options);
4034};
4035/* eslint-enable */
4036
4037
4038var getSimilarity = function getSimilarity(edge, attributes) {
4039 var total = 0;
4040
4041 for (var i = 0; i < attributes.length; i++) {
4042 total += attributes[i](edge);
4043 }
4044
4045 return total;
4046};
4047
4048var addLoops = function addLoops(M, n, val) {
4049 for (var i = 0; i < n; i++) {
4050 M[i * n + i] = val;
4051 }
4052};
4053
4054var normalize = function normalize(M, n) {
4055 var sum;
4056
4057 for (var col = 0; col < n; col++) {
4058 sum = 0;
4059
4060 for (var row = 0; row < n; row++) {
4061 sum += M[row * n + col];
4062 }
4063
4064 for (var _row = 0; _row < n; _row++) {
4065 M[_row * n + col] = M[_row * n + col] / sum;
4066 }
4067 }
4068}; // TODO: blocked matrix multiplication?
4069
4070
4071var mmult = function mmult(A, B, n) {
4072 var C = new Array(n * n);
4073
4074 for (var i = 0; i < n; i++) {
4075 for (var j = 0; j < n; j++) {
4076 C[i * n + j] = 0;
4077 }
4078
4079 for (var k = 0; k < n; k++) {
4080 for (var _j = 0; _j < n; _j++) {
4081 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4082 }
4083 }
4084 }
4085
4086 return C;
4087};
4088
4089var expand = function expand(M, n, expandFactor
4090/** power **/
4091) {
4092 var _M = M.slice(0);
4093
4094 for (var p = 1; p < expandFactor; p++) {
4095 M = mmult(M, _M, n);
4096 }
4097
4098 return M;
4099};
4100
4101var inflate = function inflate(M, n, inflateFactor
4102/** r **/
4103) {
4104 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4105
4106
4107 for (var i = 0; i < n * n; i++) {
4108 _M[i] = Math.pow(M[i], inflateFactor);
4109 }
4110
4111 normalize(_M, n);
4112 return _M;
4113};
4114
4115var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4116 // Check that both matrices have the same elements (i,j)
4117 for (var i = 0; i < n2; i++) {
4118 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4119
4120 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4121
4122 if (v1 !== v2) {
4123 return false;
4124 }
4125 }
4126
4127 return true;
4128};
4129
4130var assign = function assign(M, n, nodes, cy) {
4131 var clusters = [];
4132
4133 for (var i = 0; i < n; i++) {
4134 var cluster = [];
4135
4136 for (var j = 0; j < n; j++) {
4137 // Row-wise attractors and elements that they attract belong in same cluster
4138 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4139 cluster.push(nodes[j]);
4140 }
4141 }
4142
4143 if (cluster.length !== 0) {
4144 clusters.push(cy.collection(cluster));
4145 }
4146 }
4147
4148 return clusters;
4149};
4150
4151var isDuplicate = function isDuplicate(c1, c2) {
4152 for (var i = 0; i < c1.length; i++) {
4153 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4154 return false;
4155 }
4156 }
4157
4158 return true;
4159};
4160
4161var removeDuplicates = function removeDuplicates(clusters) {
4162 for (var i = 0; i < clusters.length; i++) {
4163 for (var j = 0; j < clusters.length; j++) {
4164 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4165 clusters.splice(j, 1);
4166 }
4167 }
4168 }
4169
4170 return clusters;
4171};
4172
4173var markovClustering = function markovClustering(options) {
4174 var nodes = this.nodes();
4175 var edges = this.edges();
4176 var cy = this.cy(); // Set parameters of algorithm:
4177
4178 var opts = setOptions(options); // Map each node to its position in node array
4179
4180 var id2position = {};
4181
4182 for (var i = 0; i < nodes.length; i++) {
4183 id2position[nodes[i].id()] = i;
4184 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4185
4186
4187 var n = nodes.length,
4188 n2 = n * n;
4189
4190 var M = new Array(n2),
4191 _M;
4192
4193 for (var _i = 0; _i < n2; _i++) {
4194 M[_i] = 0;
4195 }
4196
4197 for (var e = 0; e < edges.length; e++) {
4198 var edge = edges[e];
4199 var _i2 = id2position[edge.source().id()];
4200 var j = id2position[edge.target().id()];
4201 var sim = getSimilarity(edge, opts.attributes);
4202 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4203
4204 M[j * n + _i2] += sim;
4205 } // Begin Markov cluster algorithm
4206 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4207
4208
4209 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4210
4211 normalize(M, n);
4212 var isStillMoving = true;
4213 var iterations = 0;
4214
4215 while (isStillMoving && iterations < opts.maxIterations) {
4216 isStillMoving = false; // Step 3:
4217
4218 _M = expand(M, n, opts.expandFactor); // Step 4:
4219
4220 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4221
4222 if (!hasConverged(M, _M, n2, 4)) {
4223 isStillMoving = true;
4224 }
4225
4226 iterations++;
4227 } // Build clusters from matrix
4228
4229
4230 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4231
4232 clusters = removeDuplicates(clusters);
4233 return clusters;
4234};
4235
4236var markovClustering$1 = {
4237 markovClustering: markovClustering,
4238 mcl: markovClustering
4239};
4240
4241// Common distance metrics for clustering algorithms
4242
4243var identity = function identity(x) {
4244 return x;
4245};
4246
4247var absDiff = function absDiff(p, q) {
4248 return Math.abs(q - p);
4249};
4250
4251var addAbsDiff = function addAbsDiff(total, p, q) {
4252 return total + absDiff(p, q);
4253};
4254
4255var addSquaredDiff = function addSquaredDiff(total, p, q) {
4256 return total + Math.pow(q - p, 2);
4257};
4258
4259var sqrt = function sqrt(x) {
4260 return Math.sqrt(x);
4261};
4262
4263var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4264 return Math.max(currentMax, absDiff(p, q));
4265};
4266
4267var getDistance = function getDistance(length, getP, getQ, init, visit) {
4268 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4269 var ret = init;
4270 var p, q;
4271
4272 for (var dim = 0; dim < length; dim++) {
4273 p = getP(dim);
4274 q = getQ(dim);
4275 ret = visit(ret, p, q);
4276 }
4277
4278 return post(ret);
4279};
4280
4281var distances = {
4282 euclidean: function euclidean(length, getP, getQ) {
4283 if (length >= 2) {
4284 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4285 } else {
4286 // for single attr case, more efficient to avoid sqrt
4287 return getDistance(length, getP, getQ, 0, addAbsDiff);
4288 }
4289 },
4290 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4291 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4292 },
4293 manhattan: function manhattan(length, getP, getQ) {
4294 return getDistance(length, getP, getQ, 0, addAbsDiff);
4295 },
4296 max: function max(length, getP, getQ) {
4297 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4298 }
4299}; // in case the user accidentally doesn't use camel case
4300
4301distances['squared-euclidean'] = distances['squaredEuclidean'];
4302distances['squaredeuclidean'] = distances['squaredEuclidean'];
4303function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4304 var impl;
4305
4306 if (fn(method)) {
4307 impl = method;
4308 } else {
4309 impl = distances[method] || distances.euclidean;
4310 }
4311
4312 if (length === 0 && fn(method)) {
4313 return impl(nodeP, nodeQ);
4314 } else {
4315 return impl(length, getP, getQ, nodeP, nodeQ);
4316 }
4317}
4318
4319var defaults$5 = defaults({
4320 k: 2,
4321 m: 2,
4322 sensitivityThreshold: 0.0001,
4323 distance: 'euclidean',
4324 maxIterations: 10,
4325 attributes: [],
4326 testMode: false,
4327 testCentroids: null
4328});
4329
4330var setOptions$1 = function setOptions(options) {
4331 return defaults$5(options);
4332};
4333/* eslint-enable */
4334
4335
4336var getDist = function getDist(type, node, centroid, attributes, mode) {
4337 var noNodeP = mode !== 'kMedoids';
4338 var getP = noNodeP ? function (i) {
4339 return centroid[i];
4340 } : function (i) {
4341 return attributes[i](centroid);
4342 };
4343
4344 var getQ = function getQ(i) {
4345 return attributes[i](node);
4346 };
4347
4348 var nodeP = centroid;
4349 var nodeQ = node;
4350 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4351};
4352
4353var randomCentroids = function randomCentroids(nodes, k, attributes) {
4354 var ndim = attributes.length;
4355 var min = new Array(ndim);
4356 var max = new Array(ndim);
4357 var centroids = new Array(k);
4358 var centroid = null; // Find min, max values for each attribute dimension
4359
4360 for (var i = 0; i < ndim; i++) {
4361 min[i] = nodes.min(attributes[i]).value;
4362 max[i] = nodes.max(attributes[i]).value;
4363 } // Build k centroids, each represented as an n-dim feature vector
4364
4365
4366 for (var c = 0; c < k; c++) {
4367 centroid = [];
4368
4369 for (var _i = 0; _i < ndim; _i++) {
4370 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4371 }
4372
4373 centroids[c] = centroid;
4374 }
4375
4376 return centroids;
4377};
4378
4379var classify = function classify(node, centroids, distance, attributes, type) {
4380 var min = Infinity;
4381 var index = 0;
4382
4383 for (var i = 0; i < centroids.length; i++) {
4384 var dist = getDist(distance, node, centroids[i], attributes, type);
4385
4386 if (dist < min) {
4387 min = dist;
4388 index = i;
4389 }
4390 }
4391
4392 return index;
4393};
4394
4395var buildCluster = function buildCluster(centroid, nodes, assignment) {
4396 var cluster = [];
4397 var node = null;
4398
4399 for (var n = 0; n < nodes.length; n++) {
4400 node = nodes[n];
4401
4402 if (assignment[node.id()] === centroid) {
4403 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4404 cluster.push(node);
4405 }
4406 }
4407
4408 return cluster;
4409};
4410
4411var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4412 return Math.abs(v2 - v1) <= sensitivityThreshold;
4413};
4414
4415var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4416 for (var i = 0; i < v1.length; i++) {
4417 for (var j = 0; j < v1[i].length; j++) {
4418 var diff = Math.abs(v1[i][j] - v2[i][j]);
4419
4420 if (diff > sensitivityThreshold) {
4421 return false;
4422 }
4423 }
4424 }
4425
4426 return true;
4427};
4428
4429var seenBefore = function seenBefore(node, medoids, n) {
4430 for (var i = 0; i < n; i++) {
4431 if (node === medoids[i]) return true;
4432 }
4433
4434 return false;
4435};
4436
4437var randomMedoids = function randomMedoids(nodes, k) {
4438 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
4439 // so we need to check to see if we've already seen or chose this node before.
4440
4441 if (nodes.length < 50) {
4442 // Randomly select k medoids from the n nodes
4443 for (var i = 0; i < k; i++) {
4444 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).
4445 // Instead choose a different random node.
4446
4447 while (seenBefore(node, medoids, i)) {
4448 node = nodes[Math.floor(Math.random() * nodes.length)];
4449 }
4450
4451 medoids[i] = node;
4452 }
4453 } else {
4454 // Relatively large data set, so pretty safe to not check and just select random nodes
4455 for (var _i2 = 0; _i2 < k; _i2++) {
4456 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4457 }
4458 }
4459
4460 return medoids;
4461};
4462
4463var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4464 var cost = 0;
4465
4466 for (var n = 0; n < cluster.length; n++) {
4467 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4468 }
4469
4470 return cost;
4471};
4472
4473var kMeans = function kMeans(options) {
4474 var cy = this.cy();
4475 var nodes = this.nodes();
4476 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
4477
4478 var opts = setOptions$1(options); // Begin k-means algorithm
4479
4480 var clusters = new Array(opts.k);
4481 var assignment = {};
4482 var centroids; // Step 1: Initialize centroid positions
4483
4484 if (opts.testMode) {
4485 if (typeof opts.testCentroids === 'number') {
4486 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4487 } else if (_typeof(opts.testCentroids) === 'object') {
4488 centroids = opts.testCentroids;
4489 } else {
4490 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4491 }
4492 } else {
4493 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4494 }
4495
4496 var isStillMoving = true;
4497 var iterations = 0;
4498
4499 while (isStillMoving && iterations < opts.maxIterations) {
4500 // Step 2: Assign nodes to the nearest centroid
4501 for (var n = 0; n < nodes.length; n++) {
4502 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4503
4504 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
4505 } // Step 3: For each of the k clusters, update its centroid
4506
4507
4508 isStillMoving = false;
4509
4510 for (var c = 0; c < opts.k; c++) {
4511 // Get all nodes that belong to this cluster
4512 var cluster = buildCluster(c, nodes, assignment);
4513
4514 if (cluster.length === 0) {
4515 // If cluster is empty, break out early & move to next cluster
4516 continue;
4517 } // Update centroids by calculating avg of all nodes within the cluster.
4518
4519
4520 var ndim = opts.attributes.length;
4521 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
4522
4523 var newCentroid = new Array(ndim);
4524 var sum = new Array(ndim);
4525
4526 for (var d = 0; d < ndim; d++) {
4527 sum[d] = 0.0;
4528
4529 for (var i = 0; i < cluster.length; i++) {
4530 node = cluster[i];
4531 sum[d] += opts.attributes[d](node);
4532 }
4533
4534 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
4535
4536 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
4537 isStillMoving = true;
4538 }
4539 }
4540
4541 centroids[c] = newCentroid;
4542 clusters[c] = cy.collection(cluster);
4543 }
4544
4545 iterations++;
4546 }
4547
4548 return clusters;
4549};
4550
4551var kMedoids = function kMedoids(options) {
4552 var cy = this.cy();
4553 var nodes = this.nodes();
4554 var node = null;
4555 var opts = setOptions$1(options); // Begin k-medoids algorithm
4556
4557 var clusters = new Array(opts.k);
4558 var medoids;
4559 var assignment = {};
4560 var curCost;
4561 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
4562 // Step 1: Initialize k medoids
4563
4564 if (opts.testMode) {
4565 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
4566 medoids = opts.testCentroids;
4567 } else {
4568 medoids = randomMedoids(nodes, opts.k);
4569 }
4570 } else {
4571 medoids = randomMedoids(nodes, opts.k);
4572 }
4573
4574 var isStillMoving = true;
4575 var iterations = 0;
4576
4577 while (isStillMoving && iterations < opts.maxIterations) {
4578 // Step 2: Assign nodes to the nearest medoid
4579 for (var n = 0; n < nodes.length; n++) {
4580 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4581
4582 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
4583 }
4584
4585 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
4586 // select the node with the lowest configuration cost as new medoid.
4587
4588 for (var m = 0; m < medoids.length; m++) {
4589 // Get all nodes that belong to this medoid
4590 var cluster = buildCluster(m, nodes, assignment);
4591
4592 if (cluster.length === 0) {
4593 // If cluster is empty, break out early & move to next cluster
4594 continue;
4595 }
4596
4597 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
4598 // Select different medoid if its configuration has the lowest cost
4599
4600 for (var _n = 0; _n < cluster.length; _n++) {
4601 curCost = findCost(cluster[_n], cluster, opts.attributes);
4602
4603 if (curCost < minCosts[m]) {
4604 minCosts[m] = curCost;
4605 medoids[m] = cluster[_n];
4606 isStillMoving = true;
4607 }
4608 }
4609
4610 clusters[m] = cy.collection(cluster);
4611 }
4612
4613 iterations++;
4614 }
4615
4616 return clusters;
4617};
4618
4619var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
4620 var numerator, denominator;
4621
4622 for (var n = 0; n < nodes.length; n++) {
4623 for (var c = 0; c < centroids.length; c++) {
4624 weight[n][c] = Math.pow(U[n][c], opts.m);
4625 }
4626 }
4627
4628 for (var _c = 0; _c < centroids.length; _c++) {
4629 for (var dim = 0; dim < opts.attributes.length; dim++) {
4630 numerator = 0;
4631 denominator = 0;
4632
4633 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
4634 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
4635 denominator += weight[_n2][_c];
4636 }
4637
4638 centroids[_c][dim] = numerator / denominator;
4639 }
4640 }
4641};
4642
4643var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
4644 // Save previous step
4645 for (var i = 0; i < U.length; i++) {
4646 _U[i] = U[i].slice();
4647 }
4648
4649 var sum, numerator, denominator;
4650 var pow = 2 / (opts.m - 1);
4651
4652 for (var c = 0; c < centroids.length; c++) {
4653 for (var n = 0; n < nodes.length; n++) {
4654 sum = 0;
4655
4656 for (var k = 0; k < centroids.length; k++) {
4657 // against all other centroids
4658 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
4659 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
4660 sum += Math.pow(numerator / denominator, pow);
4661 }
4662
4663 U[n][c] = 1 / sum;
4664 }
4665 }
4666};
4667
4668var assign$1 = function assign(nodes, U, opts, cy) {
4669 var clusters = new Array(opts.k);
4670
4671 for (var c = 0; c < clusters.length; c++) {
4672 clusters[c] = [];
4673 }
4674
4675 var max;
4676 var index;
4677
4678 for (var n = 0; n < U.length; n++) {
4679 // for each node (U is N x C matrix)
4680 max = -Infinity;
4681 index = -1; // Determine which cluster the node is most likely to belong in
4682
4683 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
4684 if (U[n][_c2] > max) {
4685 max = U[n][_c2];
4686 index = _c2;
4687 }
4688 }
4689
4690 clusters[index].push(nodes[n]);
4691 } // Turn every array into a collection of nodes
4692
4693
4694 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
4695 clusters[_c3] = cy.collection(clusters[_c3]);
4696 }
4697
4698 return clusters;
4699};
4700
4701var fuzzyCMeans = function fuzzyCMeans(options) {
4702 var cy = this.cy();
4703 var nodes = this.nodes();
4704 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
4705
4706 var clusters;
4707 var centroids;
4708 var U;
4709
4710 var _U;
4711
4712 var weight; // Step 1: Initialize letiables.
4713
4714 _U = new Array(nodes.length);
4715
4716 for (var i = 0; i < nodes.length; i++) {
4717 // N x C matrix
4718 _U[i] = new Array(opts.k);
4719 }
4720
4721 U = new Array(nodes.length);
4722
4723 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
4724 // N x C matrix
4725 U[_i3] = new Array(opts.k);
4726 }
4727
4728 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
4729 var total = 0;
4730
4731 for (var j = 0; j < opts.k; j++) {
4732 U[_i4][j] = Math.random();
4733 total += U[_i4][j];
4734 }
4735
4736 for (var _j = 0; _j < opts.k; _j++) {
4737 U[_i4][_j] = U[_i4][_j] / total;
4738 }
4739 }
4740
4741 centroids = new Array(opts.k);
4742
4743 for (var _i5 = 0; _i5 < opts.k; _i5++) {
4744 centroids[_i5] = new Array(opts.attributes.length);
4745 }
4746
4747 weight = new Array(nodes.length);
4748
4749 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
4750 // N x C matrix
4751 weight[_i6] = new Array(opts.k);
4752 } // end init FCM
4753
4754
4755 var isStillMoving = true;
4756 var iterations = 0;
4757
4758 while (isStillMoving && iterations < opts.maxIterations) {
4759 isStillMoving = false; // Step 2: Calculate the centroids for each step.
4760
4761 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
4762
4763 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
4764
4765 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
4766 isStillMoving = true;
4767 }
4768
4769 iterations++;
4770 } // Assign nodes to clusters with highest probability.
4771
4772
4773 clusters = assign$1(nodes, U, opts, cy);
4774 return {
4775 clusters: clusters,
4776 degreeOfMembership: U
4777 };
4778};
4779
4780var kClustering = {
4781 kMeans: kMeans,
4782 kMedoids: kMedoids,
4783 fuzzyCMeans: fuzzyCMeans,
4784 fcm: fuzzyCMeans
4785};
4786
4787// Implemented by Zoe Xi @zoexi for GSOC 2016
4788var defaults$6 = defaults({
4789 distance: 'euclidean',
4790 // distance metric to compare nodes
4791 linkage: 'min',
4792 // linkage criterion : how to determine the distance between clusters of nodes
4793 mode: 'threshold',
4794 // mode:'threshold' => clusters must be threshold distance apart
4795 threshold: Infinity,
4796 // the distance threshold
4797 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
4798 addDendrogram: false,
4799 // whether to add the dendrogram to the graph for viz
4800 dendrogramDepth: 0,
4801 // depth at which dendrogram branches are merged into the returned clusters
4802 attributes: [] // array of attr functions
4803
4804});
4805var linkageAliases = {
4806 'single': 'min',
4807 'complete': 'max'
4808};
4809
4810var setOptions$2 = function setOptions(options) {
4811 var opts = defaults$6(options);
4812 var preferredAlias = linkageAliases[opts.linkage];
4813
4814 if (preferredAlias != null) {
4815 opts.linkage = preferredAlias;
4816 }
4817
4818 return opts;
4819};
4820
4821var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
4822 // Find two closest clusters from cached mins
4823 var minKey = 0;
4824 var min = Infinity;
4825 var dist;
4826 var attrs = opts.attributes;
4827
4828 var getDist = function getDist(n1, n2) {
4829 return clusteringDistance(opts.distance, attrs.length, function (i) {
4830 return attrs[i](n1);
4831 }, function (i) {
4832 return attrs[i](n2);
4833 }, n1, n2);
4834 };
4835
4836 for (var i = 0; i < clusters.length; i++) {
4837 var key = clusters[i].key;
4838 var _dist = dists[key][mins[key]];
4839
4840 if (_dist < min) {
4841 minKey = key;
4842 min = _dist;
4843 }
4844 }
4845
4846 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
4847 return false;
4848 }
4849
4850 var c1 = index[minKey];
4851 var c2 = index[mins[minKey]];
4852 var merged; // Merge two closest clusters
4853
4854 if (opts.mode === 'dendrogram') {
4855 merged = {
4856 left: c1,
4857 right: c2,
4858 key: c1.key
4859 };
4860 } else {
4861 merged = {
4862 value: c1.value.concat(c2.value),
4863 key: c1.key
4864 };
4865 }
4866
4867 clusters[c1.index] = merged;
4868 clusters.splice(c2.index, 1);
4869 index[c1.key] = merged; // Update distances with new merged cluster
4870
4871 for (var _i = 0; _i < clusters.length; _i++) {
4872 var cur = clusters[_i];
4873
4874 if (c1.key === cur.key) {
4875 dist = Infinity;
4876 } else if (opts.linkage === 'min') {
4877 dist = dists[c1.key][cur.key];
4878
4879 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
4880 dist = dists[c2.key][cur.key];
4881 }
4882 } else if (opts.linkage === 'max') {
4883 dist = dists[c1.key][cur.key];
4884
4885 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
4886 dist = dists[c2.key][cur.key];
4887 }
4888 } else if (opts.linkage === 'mean') {
4889 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
4890 } else {
4891 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
4892 }
4893
4894 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
4895 } // Update cached mins
4896
4897
4898 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
4899 var key1 = clusters[_i2].key;
4900
4901 if (mins[key1] === c1.key || mins[key1] === c2.key) {
4902 var _min = key1;
4903
4904 for (var j = 0; j < clusters.length; j++) {
4905 var key2 = clusters[j].key;
4906
4907 if (dists[key1][key2] < dists[key1][_min]) {
4908 _min = key2;
4909 }
4910 }
4911
4912 mins[key1] = _min;
4913 }
4914
4915 clusters[_i2].index = _i2;
4916 } // Clean up meta data used for clustering
4917
4918
4919 c1.key = c2.key = c1.index = c2.index = null;
4920 return true;
4921};
4922
4923var getAllChildren = function getAllChildren(root, arr, cy) {
4924 if (!root) return;
4925
4926 if (root.value) {
4927 arr.push(root.value);
4928 } else {
4929 if (root.left) getAllChildren(root.left, arr);
4930 if (root.right) getAllChildren(root.right, arr);
4931 }
4932};
4933
4934var buildDendrogram = function buildDendrogram(root, cy) {
4935 if (!root) return '';
4936
4937 if (root.left && root.right) {
4938 var leftStr = buildDendrogram(root.left, cy);
4939 var rightStr = buildDendrogram(root.right, cy);
4940 var node = cy.add({
4941 group: 'nodes',
4942 data: {
4943 id: leftStr + ',' + rightStr
4944 }
4945 });
4946 cy.add({
4947 group: 'edges',
4948 data: {
4949 source: leftStr,
4950 target: node.id()
4951 }
4952 });
4953 cy.add({
4954 group: 'edges',
4955 data: {
4956 source: rightStr,
4957 target: node.id()
4958 }
4959 });
4960 return node.id();
4961 } else if (root.value) {
4962 return root.value.id();
4963 }
4964};
4965
4966var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
4967 if (!root) return [];
4968 var left = [],
4969 right = [],
4970 leaves = [];
4971
4972 if (k === 0) {
4973 // don't cut tree, simply return all nodes as 1 single cluster
4974 if (root.left) getAllChildren(root.left, left);
4975 if (root.right) getAllChildren(root.right, right);
4976 leaves = left.concat(right);
4977 return [cy.collection(leaves)];
4978 } else if (k === 1) {
4979 // cut at root
4980 if (root.value) {
4981 // leaf node
4982 return [cy.collection(root.value)];
4983 } else {
4984 if (root.left) getAllChildren(root.left, left);
4985 if (root.right) getAllChildren(root.right, right);
4986 return [cy.collection(left), cy.collection(right)];
4987 }
4988 } else {
4989 if (root.value) {
4990 return [cy.collection(root.value)];
4991 } else {
4992 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
4993 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
4994 return left.concat(right);
4995 }
4996 }
4997};
4998/* eslint-enable */
4999
5000
5001var hierarchicalClustering = function hierarchicalClustering(options) {
5002 var cy = this.cy();
5003 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
5004
5005 var opts = setOptions$2(options);
5006 var attrs = opts.attributes;
5007
5008 var getDist = function getDist(n1, n2) {
5009 return clusteringDistance(opts.distance, attrs.length, function (i) {
5010 return attrs[i](n1);
5011 }, function (i) {
5012 return attrs[i](n2);
5013 }, n1, n2);
5014 }; // Begin hierarchical algorithm
5015
5016
5017 var clusters = [];
5018 var dists = []; // distances between each pair of clusters
5019
5020 var mins = []; // closest cluster for each cluster
5021
5022 var index = []; // hash of all clusters by key
5023 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5024
5025 for (var n = 0; n < nodes.length; n++) {
5026 var cluster = {
5027 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5028 key: n,
5029 index: n
5030 };
5031 clusters[n] = cluster;
5032 index[n] = cluster;
5033 dists[n] = [];
5034 mins[n] = 0;
5035 } // Calculate the distance between each pair of clusters
5036
5037
5038 for (var i = 0; i < clusters.length; i++) {
5039 for (var j = 0; j <= i; j++) {
5040 var dist = void 0;
5041
5042 if (opts.mode === 'dendrogram') {
5043 // modes store cluster values differently
5044 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5045 } else {
5046 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5047 }
5048
5049 dists[i][j] = dist;
5050 dists[j][i] = dist;
5051
5052 if (dist < dists[i][mins[i]]) {
5053 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5054 }
5055 }
5056 } // Find the closest pair of clusters and merge them into a single cluster.
5057 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5058
5059
5060 var merged = mergeClosest(clusters, index, dists, mins, opts);
5061
5062 while (merged) {
5063 merged = mergeClosest(clusters, index, dists, mins, opts);
5064 }
5065
5066 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5067 // in addition to returning the clusters.
5068
5069 if (opts.mode === 'dendrogram') {
5070 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5071 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5072 } else {
5073 // Regular mode simply returns the clusters
5074 retClusters = new Array(clusters.length);
5075 clusters.forEach(function (cluster, i) {
5076 // Clean up meta data used for clustering
5077 cluster.key = cluster.index = null;
5078 retClusters[i] = cy.collection(cluster.value);
5079 });
5080 }
5081
5082 return retClusters;
5083};
5084
5085var hierarchicalClustering$1 = {
5086 hierarchicalClustering: hierarchicalClustering,
5087 hca: hierarchicalClustering
5088};
5089
5090// Implemented by Zoe Xi @zoexi for GSOC 2016
5091var defaults$7 = defaults({
5092 distance: 'euclidean',
5093 // distance metric to compare attributes between two nodes
5094 preference: 'median',
5095 // suitability of a data point to serve as an exemplar
5096 damping: 0.8,
5097 // damping factor between [0.5, 1)
5098 maxIterations: 1000,
5099 // max number of iterations to run
5100 minIterations: 100,
5101 // min number of iterations to run in order for clustering to stop
5102 attributes: [// functions to quantify the similarity between any two points
5103 // e.g. node => node.data('weight')
5104 ]
5105});
5106
5107var setOptions$3 = function setOptions(options) {
5108 var dmp = options.damping;
5109 var pref = options.preference;
5110
5111 if (!(0.5 <= dmp && dmp < 1)) {
5112 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5113 }
5114
5115 var validPrefs = ['median', 'mean', 'min', 'max'];
5116
5117 if (!(validPrefs.some(function (v) {
5118 return v === pref;
5119 }) || number(pref))) {
5120 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5121 return "'".concat(p, "'");
5122 }).join(', '), "] or a number. Got: ").concat(pref));
5123 }
5124
5125 return defaults$7(options);
5126};
5127/* eslint-enable */
5128
5129
5130var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5131 var attr = function attr(n, i) {
5132 return attributes[i](n);
5133 }; // nb negative because similarity should have an inverse relationship to distance
5134
5135
5136 return -clusteringDistance(type, attributes.length, function (i) {
5137 return attr(n1, i);
5138 }, function (i) {
5139 return attr(n2, i);
5140 }, n1, n2);
5141};
5142
5143var getPreference = function getPreference(S, preference) {
5144 // larger preference = greater # of clusters
5145 var p = null;
5146
5147 if (preference === 'median') {
5148 p = median(S);
5149 } else if (preference === 'mean') {
5150 p = mean(S);
5151 } else if (preference === 'min') {
5152 p = min(S);
5153 } else if (preference === 'max') {
5154 p = max(S);
5155 } else {
5156 // Custom preference number, as set by user
5157 p = preference;
5158 }
5159
5160 return p;
5161};
5162
5163var findExemplars = function findExemplars(n, R, A) {
5164 var indices = [];
5165
5166 for (var i = 0; i < n; i++) {
5167 if (R[i * n + i] + A[i * n + i] > 0) {
5168 indices.push(i);
5169 }
5170 }
5171
5172 return indices;
5173};
5174
5175var assignClusters = function assignClusters(n, S, exemplars) {
5176 var clusters = [];
5177
5178 for (var i = 0; i < n; i++) {
5179 var index = -1;
5180 var max = -Infinity;
5181
5182 for (var ei = 0; ei < exemplars.length; ei++) {
5183 var e = exemplars[ei];
5184
5185 if (S[i * n + e] > max) {
5186 index = e;
5187 max = S[i * n + e];
5188 }
5189 }
5190
5191 if (index > 0) {
5192 clusters.push(index);
5193 }
5194 }
5195
5196 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5197 clusters[exemplars[_ei]] = exemplars[_ei];
5198 }
5199
5200 return clusters;
5201};
5202
5203var assign$2 = function assign(n, S, exemplars) {
5204 var clusters = assignClusters(n, S, exemplars);
5205
5206 for (var ei = 0; ei < exemplars.length; ei++) {
5207 var ii = [];
5208
5209 for (var c = 0; c < clusters.length; c++) {
5210 if (clusters[c] === exemplars[ei]) {
5211 ii.push(c);
5212 }
5213 }
5214
5215 var maxI = -1;
5216 var maxSum = -Infinity;
5217
5218 for (var i = 0; i < ii.length; i++) {
5219 var sum = 0;
5220
5221 for (var j = 0; j < ii.length; j++) {
5222 sum += S[ii[j] * n + ii[i]];
5223 }
5224
5225 if (sum > maxSum) {
5226 maxI = i;
5227 maxSum = sum;
5228 }
5229 }
5230
5231 exemplars[ei] = ii[maxI];
5232 }
5233
5234 clusters = assignClusters(n, S, exemplars);
5235 return clusters;
5236};
5237
5238var affinityPropagation = function affinityPropagation(options) {
5239 var cy = this.cy();
5240 var nodes = this.nodes();
5241 var opts = setOptions$3(options); // Map each node to its position in node array
5242
5243 var id2position = {};
5244
5245 for (var i = 0; i < nodes.length; i++) {
5246 id2position[nodes[i].id()] = i;
5247 } // Begin affinity propagation algorithm
5248
5249
5250 var n; // number of data points
5251
5252 var n2; // size of matrices
5253
5254 var S; // similarity matrix (1D array)
5255
5256 var p; // preference/suitability of a data point to serve as an exemplar
5257
5258 var R; // responsibility matrix (1D array)
5259
5260 var A; // availability matrix (1D array)
5261
5262 n = nodes.length;
5263 n2 = n * n; // Initialize and build S similarity matrix
5264
5265 S = new Array(n2);
5266
5267 for (var _i = 0; _i < n2; _i++) {
5268 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5269 }
5270
5271 for (var _i2 = 0; _i2 < n; _i2++) {
5272 for (var j = 0; j < n; j++) {
5273 if (_i2 !== j) {
5274 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5275 }
5276 }
5277 } // Place preferences on the diagonal of S
5278
5279
5280 p = getPreference(S, opts.preference);
5281
5282 for (var _i3 = 0; _i3 < n; _i3++) {
5283 S[_i3 * n + _i3] = p;
5284 } // Initialize R responsibility matrix
5285
5286
5287 R = new Array(n2);
5288
5289 for (var _i4 = 0; _i4 < n2; _i4++) {
5290 R[_i4] = 0.0;
5291 } // Initialize A availability matrix
5292
5293
5294 A = new Array(n2);
5295
5296 for (var _i5 = 0; _i5 < n2; _i5++) {
5297 A[_i5] = 0.0;
5298 }
5299
5300 var old = new Array(n);
5301 var Rp = new Array(n);
5302 var se = new Array(n);
5303
5304 for (var _i6 = 0; _i6 < n; _i6++) {
5305 old[_i6] = 0.0;
5306 Rp[_i6] = 0.0;
5307 se[_i6] = 0;
5308 }
5309
5310 var e = new Array(n * opts.minIterations);
5311
5312 for (var _i7 = 0; _i7 < e.length; _i7++) {
5313 e[_i7] = 0;
5314 }
5315
5316 var iter;
5317
5318 for (iter = 0; iter < opts.maxIterations; iter++) {
5319 // main algorithmic loop
5320 // Update R responsibility matrix
5321 for (var _i8 = 0; _i8 < n; _i8++) {
5322 var max = -Infinity,
5323 max2 = -Infinity,
5324 maxI = -1,
5325 AS = 0.0;
5326
5327 for (var _j = 0; _j < n; _j++) {
5328 old[_j] = R[_i8 * n + _j];
5329 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5330
5331 if (AS >= max) {
5332 max2 = max;
5333 max = AS;
5334 maxI = _j;
5335 } else if (AS > max2) {
5336 max2 = AS;
5337 }
5338 }
5339
5340 for (var _j2 = 0; _j2 < n; _j2++) {
5341 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5342 }
5343
5344 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5345 } // Update A availability matrix
5346
5347
5348 for (var _i9 = 0; _i9 < n; _i9++) {
5349 var sum = 0;
5350
5351 for (var _j3 = 0; _j3 < n; _j3++) {
5352 old[_j3] = A[_j3 * n + _i9];
5353 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5354 sum += Rp[_j3];
5355 }
5356
5357 sum -= Rp[_i9];
5358 Rp[_i9] = R[_i9 * n + _i9];
5359 sum += Rp[_i9];
5360
5361 for (var _j4 = 0; _j4 < n; _j4++) {
5362 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5363 }
5364
5365 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5366 } // Check for convergence
5367
5368
5369 var K = 0;
5370
5371 for (var _i10 = 0; _i10 < n; _i10++) {
5372 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5373 e[iter % opts.minIterations * n + _i10] = E;
5374 K += E;
5375 }
5376
5377 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5378 var _sum = 0;
5379
5380 for (var _i11 = 0; _i11 < n; _i11++) {
5381 se[_i11] = 0;
5382
5383 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5384 se[_i11] += e[_j5 * n + _i11];
5385 }
5386
5387 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5388 _sum++;
5389 }
5390 }
5391
5392 if (_sum === n) {
5393 // then we have convergence
5394 break;
5395 }
5396 }
5397 } // Identify exemplars (cluster centers)
5398
5399
5400 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
5401
5402 var clusterIndices = assign$2(n, S, exemplarsIndices);
5403 var clusters = {};
5404
5405 for (var c = 0; c < exemplarsIndices.length; c++) {
5406 clusters[exemplarsIndices[c]] = [];
5407 }
5408
5409 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5410 var pos = id2position[nodes[_i12].id()];
5411
5412 var clusterIndex = clusterIndices[pos];
5413
5414 if (clusterIndex != null) {
5415 // the node may have not been assigned a cluster if no valid attributes were specified
5416 clusters[clusterIndex].push(nodes[_i12]);
5417 }
5418 }
5419
5420 var retClusters = new Array(exemplarsIndices.length);
5421
5422 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5423 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5424 }
5425
5426 return retClusters;
5427};
5428
5429var affinityPropagation$1 = {
5430 affinityPropagation: affinityPropagation,
5431 ap: affinityPropagation
5432};
5433
5434var hierholzerDefaults = defaults({
5435 root: undefined,
5436 directed: false
5437});
5438var elesfn$b = {
5439 hierholzer: function hierholzer(options) {
5440 if (!plainObject(options)) {
5441 var args = arguments;
5442 options = {
5443 root: args[0],
5444 directed: args[1]
5445 };
5446 }
5447
5448 var _hierholzerDefaults = hierholzerDefaults(options),
5449 root = _hierholzerDefaults.root,
5450 directed = _hierholzerDefaults.directed;
5451
5452 var eles = this;
5453 var dflag = false;
5454 var oddIn;
5455 var oddOut;
5456 var startVertex;
5457 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5458 var nodes = {};
5459 var edges = {};
5460
5461 if (directed) {
5462 eles.forEach(function (ele) {
5463 var id = ele.id();
5464
5465 if (ele.isNode()) {
5466 var ind = ele.indegree(true);
5467 var outd = ele.outdegree(true);
5468 var d1 = ind - outd;
5469 var d2 = outd - ind;
5470
5471 if (d1 == 1) {
5472 if (oddIn) dflag = true;else oddIn = id;
5473 } else if (d2 == 1) {
5474 if (oddOut) dflag = true;else oddOut = id;
5475 } else if (d2 > 1 || d1 > 1) {
5476 dflag = true;
5477 }
5478
5479 nodes[id] = [];
5480 ele.outgoers().forEach(function (e) {
5481 if (e.isEdge()) nodes[id].push(e.id());
5482 });
5483 } else {
5484 edges[id] = [undefined, ele.target().id()];
5485 }
5486 });
5487 } else {
5488 eles.forEach(function (ele) {
5489 var id = ele.id();
5490
5491 if (ele.isNode()) {
5492 var d = ele.degree(true);
5493
5494 if (d % 2) {
5495 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5496 }
5497
5498 nodes[id] = [];
5499 ele.connectedEdges().forEach(function (e) {
5500 return nodes[id].push(e.id());
5501 });
5502 } else {
5503 edges[id] = [ele.source().id(), ele.target().id()];
5504 }
5505 });
5506 }
5507
5508 var result = {
5509 found: false,
5510 trail: undefined
5511 };
5512 if (dflag) return result;else if (oddOut && oddIn) {
5513 if (directed) {
5514 if (startVertex && oddOut != startVertex) {
5515 return result;
5516 }
5517
5518 startVertex = oddOut;
5519 } else {
5520 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5521 return result;
5522 } else if (!startVertex) {
5523 startVertex = oddOut;
5524 }
5525 }
5526 } else {
5527 if (!startVertex) startVertex = eles[0].id();
5528 }
5529
5530 var walk = function walk(v) {
5531 var currentNode = v;
5532 var subtour = [v];
5533 var adj, adjTail, adjHead;
5534
5535 while (nodes[currentNode].length) {
5536 adj = nodes[currentNode].shift();
5537 adjTail = edges[adj][0];
5538 adjHead = edges[adj][1];
5539
5540 if (currentNode != adjHead) {
5541 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5542 return e != adj;
5543 });
5544 currentNode = adjHead;
5545 } else if (!directed && currentNode != adjTail) {
5546 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5547 return e != adj;
5548 });
5549 currentNode = adjTail;
5550 }
5551
5552 subtour.unshift(adj);
5553 subtour.unshift(currentNode);
5554 }
5555
5556 return subtour;
5557 };
5558
5559 var trail = [];
5560 var subtour = [];
5561 subtour = walk(startVertex);
5562
5563 while (subtour.length != 1) {
5564 if (nodes[subtour[0]].length == 0) {
5565 trail.unshift(eles.getElementById(subtour.shift()));
5566 trail.unshift(eles.getElementById(subtour.shift()));
5567 } else {
5568 subtour = walk(subtour.shift()).concat(subtour);
5569 }
5570 }
5571
5572 trail.unshift(eles.getElementById(subtour.shift())); // final node
5573
5574 for (var d in nodes) {
5575 if (nodes[d].length) {
5576 return result;
5577 }
5578 }
5579
5580 result.found = true;
5581 result.trail = this.spawn(trail, true);
5582 return result;
5583 }
5584};
5585
5586var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5587 var eles = this;
5588 var nodes = {};
5589 var id = 0;
5590 var edgeCount = 0;
5591 var components = [];
5592 var stack = [];
5593 var visitedEdges = {};
5594
5595 var buildComponent = function buildComponent(x, y) {
5596 var i = stack.length - 1;
5597 var cutset = [];
5598 var component = eles.spawn();
5599
5600 while (stack[i].x != x || stack[i].y != y) {
5601 cutset.push(stack.pop().edge);
5602 i--;
5603 }
5604
5605 cutset.push(stack.pop().edge);
5606 cutset.forEach(function (edge) {
5607 var connectedNodes = edge.connectedNodes().intersection(eles);
5608 component.merge(edge);
5609 connectedNodes.forEach(function (node) {
5610 var nodeId = node.id();
5611 var connectedEdges = node.connectedEdges().intersection(eles);
5612 component.merge(node);
5613
5614 if (!nodes[nodeId].cutVertex) {
5615 component.merge(connectedEdges);
5616 } else {
5617 component.merge(connectedEdges.filter(function (edge) {
5618 return edge.isLoop();
5619 }));
5620 }
5621 });
5622 });
5623 components.push(component);
5624 };
5625
5626 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5627 if (root === parent) edgeCount += 1;
5628 nodes[currentNode] = {
5629 id: id,
5630 low: id++,
5631 cutVertex: false
5632 };
5633 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5634
5635 if (edges.size() === 0) {
5636 components.push(eles.spawn(eles.getElementById(currentNode)));
5637 } else {
5638 var sourceId, targetId, otherNodeId, edgeId;
5639 edges.forEach(function (edge) {
5640 sourceId = edge.source().id();
5641 targetId = edge.target().id();
5642 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5643
5644 if (otherNodeId !== parent) {
5645 edgeId = edge.id();
5646
5647 if (!visitedEdges[edgeId]) {
5648 visitedEdges[edgeId] = true;
5649 stack.push({
5650 x: currentNode,
5651 y: otherNodeId,
5652 edge: edge
5653 });
5654 }
5655
5656 if (!(otherNodeId in nodes)) {
5657 biconnectedSearch(root, otherNodeId, currentNode);
5658 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
5659
5660 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
5661 nodes[currentNode].cutVertex = true;
5662 buildComponent(currentNode, otherNodeId);
5663 }
5664 } else {
5665 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
5666 }
5667 }
5668 });
5669 }
5670 };
5671
5672 eles.forEach(function (ele) {
5673 if (ele.isNode()) {
5674 var nodeId = ele.id();
5675
5676 if (!(nodeId in nodes)) {
5677 edgeCount = 0;
5678 biconnectedSearch(nodeId, nodeId);
5679 nodes[nodeId].cutVertex = edgeCount > 1;
5680 }
5681 }
5682 });
5683 var cutVertices = Object.keys(nodes).filter(function (id) {
5684 return nodes[id].cutVertex;
5685 }).map(function (id) {
5686 return eles.getElementById(id);
5687 });
5688 return {
5689 cut: eles.spawn(cutVertices),
5690 components: components
5691 };
5692};
5693
5694var hopcroftTarjanBiconnected$1 = {
5695 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
5696 htbc: hopcroftTarjanBiconnected,
5697 htb: hopcroftTarjanBiconnected,
5698 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
5699};
5700
5701var tarjanStronglyConnected = function tarjanStronglyConnected() {
5702 var eles = this;
5703 var nodes = {};
5704 var index = 0;
5705 var components = [];
5706 var stack = [];
5707 var cut = eles.spawn(eles);
5708
5709 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
5710 stack.push(sourceNodeId);
5711 nodes[sourceNodeId] = {
5712 index: index,
5713 low: index++,
5714 explored: false
5715 };
5716 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
5717 connectedEdges.forEach(function (edge) {
5718 var targetNodeId = edge.target().id();
5719
5720 if (targetNodeId !== sourceNodeId) {
5721 if (!(targetNodeId in nodes)) {
5722 stronglyConnectedSearch(targetNodeId);
5723 }
5724
5725 if (!nodes[targetNodeId].explored) {
5726 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
5727 }
5728 }
5729 });
5730
5731 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
5732 var componentNodes = eles.spawn();
5733
5734 for (;;) {
5735 var nodeId = stack.pop();
5736 componentNodes.merge(eles.getElementById(nodeId));
5737 nodes[nodeId].low = nodes[sourceNodeId].index;
5738 nodes[nodeId].explored = true;
5739
5740 if (nodeId === sourceNodeId) {
5741 break;
5742 }
5743 }
5744
5745 var componentEdges = componentNodes.edgesWith(componentNodes);
5746 var component = componentNodes.merge(componentEdges);
5747 components.push(component);
5748 cut = cut.difference(component);
5749 }
5750 };
5751
5752 eles.forEach(function (ele) {
5753 if (ele.isNode()) {
5754 var nodeId = ele.id();
5755
5756 if (!(nodeId in nodes)) {
5757 stronglyConnectedSearch(nodeId);
5758 }
5759 }
5760 });
5761 return {
5762 cut: cut,
5763 components: components
5764 };
5765};
5766
5767var tarjanStronglyConnected$1 = {
5768 tarjanStronglyConnected: tarjanStronglyConnected,
5769 tsc: tarjanStronglyConnected,
5770 tscc: tarjanStronglyConnected,
5771 tarjanStronglyConnectedComponents: tarjanStronglyConnected
5772};
5773
5774var elesfn$c = {};
5775[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) {
5776 extend(elesfn$c, props);
5777});
5778
5779/*!
5780Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
5781Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
5782Licensed under The MIT License (http://opensource.org/licenses/MIT)
5783*/
5784
5785/* promise states [Promises/A+ 2.1] */
5786var STATE_PENDING = 0;
5787/* [Promises/A+ 2.1.1] */
5788
5789var STATE_FULFILLED = 1;
5790/* [Promises/A+ 2.1.2] */
5791
5792var STATE_REJECTED = 2;
5793/* [Promises/A+ 2.1.3] */
5794
5795/* promise object constructor */
5796
5797var api = function api(executor) {
5798 /* optionally support non-constructor/plain-function call */
5799 if (!(this instanceof api)) return new api(executor);
5800 /* initialize object */
5801
5802 this.id = 'Thenable/1.0.7';
5803 this.state = STATE_PENDING;
5804 /* initial state */
5805
5806 this.fulfillValue = undefined;
5807 /* initial value */
5808
5809 /* [Promises/A+ 1.3, 2.1.2.2] */
5810
5811 this.rejectReason = undefined;
5812 /* initial reason */
5813
5814 /* [Promises/A+ 1.5, 2.1.3.2] */
5815
5816 this.onFulfilled = [];
5817 /* initial handlers */
5818
5819 this.onRejected = [];
5820 /* initial handlers */
5821
5822 /* provide optional information-hiding proxy */
5823
5824 this.proxy = {
5825 then: this.then.bind(this)
5826 };
5827 /* support optional executor function */
5828
5829 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
5830};
5831/* promise API methods */
5832
5833
5834api.prototype = {
5835 /* promise resolving methods */
5836 fulfill: function fulfill(value) {
5837 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
5838 },
5839 reject: function reject(value) {
5840 return deliver(this, STATE_REJECTED, 'rejectReason', value);
5841 },
5842
5843 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
5844 then: function then(onFulfilled, onRejected) {
5845 var curr = this;
5846 var next = new api();
5847 /* [Promises/A+ 2.2.7] */
5848
5849 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
5850 /* [Promises/A+ 2.2.2/2.2.6] */
5851
5852 curr.onRejected.push(resolver(onRejected, next, 'reject'));
5853 /* [Promises/A+ 2.2.3/2.2.6] */
5854
5855 execute(curr);
5856 return next.proxy;
5857 /* [Promises/A+ 2.2.7, 3.3] */
5858 }
5859};
5860/* deliver an action */
5861
5862var deliver = function deliver(curr, state, name, value) {
5863 if (curr.state === STATE_PENDING) {
5864 curr.state = state;
5865 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
5866
5867 curr[name] = value;
5868 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
5869
5870 execute(curr);
5871 }
5872
5873 return curr;
5874};
5875/* execute all handlers */
5876
5877
5878var execute = function execute(curr) {
5879 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
5880};
5881/* execute particular set of handlers */
5882
5883
5884var execute_handlers = function execute_handlers(curr, name, value) {
5885 /* global setImmediate: true */
5886
5887 /* global setTimeout: true */
5888
5889 /* short-circuit processing */
5890 if (curr[name].length === 0) return;
5891 /* iterate over all handlers, exactly once */
5892
5893 var handlers = curr[name];
5894 curr[name] = [];
5895 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
5896
5897 var func = function func() {
5898 for (var i = 0; i < handlers.length; i++) {
5899 handlers[i](value);
5900 }
5901 /* [Promises/A+ 2.2.5] */
5902
5903 };
5904 /* execute procedure asynchronously */
5905
5906 /* [Promises/A+ 2.2.4, 3.1] */
5907
5908
5909 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
5910};
5911/* generate a resolver function */
5912
5913
5914var resolver = function resolver(cb, next, method) {
5915 return function (value) {
5916 if (typeof cb !== 'function')
5917 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
5918 next[method].call(next, value);
5919 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
5920 else {
5921 var result;
5922
5923 try {
5924 result = cb(value);
5925 }
5926 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
5927 catch (e) {
5928 next.reject(e);
5929 /* [Promises/A+ 2.2.7.2] */
5930
5931 return;
5932 }
5933
5934 resolve(next, result);
5935 /* [Promises/A+ 2.2.7.1] */
5936 }
5937 };
5938};
5939/* "Promise Resolution Procedure" */
5940
5941/* [Promises/A+ 2.3] */
5942
5943
5944var resolve = function resolve(promise, x) {
5945 /* sanity check arguments */
5946
5947 /* [Promises/A+ 2.3.1] */
5948 if (promise === x || promise.proxy === x) {
5949 promise.reject(new TypeError('cannot resolve promise with itself'));
5950 return;
5951 }
5952 /* surgically check for a "then" method
5953 (mainly to just call the "getter" of "then" only once) */
5954
5955
5956 var then;
5957
5958 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
5959 try {
5960 then = x.then;
5961 }
5962 /* [Promises/A+ 2.3.3.1, 3.5] */
5963 catch (e) {
5964 promise.reject(e);
5965 /* [Promises/A+ 2.3.3.2] */
5966
5967 return;
5968 }
5969 }
5970 /* handle own Thenables [Promises/A+ 2.3.2]
5971 and similar "thenables" [Promises/A+ 2.3.3] */
5972
5973
5974 if (typeof then === 'function') {
5975 var resolved = false;
5976
5977 try {
5978 /* call retrieved "then" method */
5979
5980 /* [Promises/A+ 2.3.3.3] */
5981 then.call(x,
5982 /* resolvePromise */
5983
5984 /* [Promises/A+ 2.3.3.3.1] */
5985 function (y) {
5986 if (resolved) return;
5987 resolved = true;
5988 /* [Promises/A+ 2.3.3.3.3] */
5989
5990 if (y === x)
5991 /* [Promises/A+ 3.6] */
5992 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
5993 },
5994 /* rejectPromise */
5995
5996 /* [Promises/A+ 2.3.3.3.2] */
5997 function (r) {
5998 if (resolved) return;
5999 resolved = true;
6000 /* [Promises/A+ 2.3.3.3.3] */
6001
6002 promise.reject(r);
6003 });
6004 } catch (e) {
6005 if (!resolved)
6006 /* [Promises/A+ 2.3.3.3.3] */
6007 promise.reject(e);
6008 /* [Promises/A+ 2.3.3.3.4] */
6009 }
6010
6011 return;
6012 }
6013 /* handle other values */
6014
6015
6016 promise.fulfill(x);
6017 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6018}; // so we always have Promise.all()
6019
6020
6021api.all = function (ps) {
6022 return new api(function (resolveAll, rejectAll) {
6023 var vals = new Array(ps.length);
6024 var doneCount = 0;
6025
6026 var fulfill = function fulfill(i, val) {
6027 vals[i] = val;
6028 doneCount++;
6029
6030 if (doneCount === ps.length) {
6031 resolveAll(vals);
6032 }
6033 };
6034
6035 for (var i = 0; i < ps.length; i++) {
6036 (function (i) {
6037 var p = ps[i];
6038 var isPromise = p != null && p.then != null;
6039
6040 if (isPromise) {
6041 p.then(function (val) {
6042 fulfill(i, val);
6043 }, function (err) {
6044 rejectAll(err);
6045 });
6046 } else {
6047 var val = p;
6048 fulfill(i, val);
6049 }
6050 })(i);
6051 }
6052 });
6053};
6054
6055api.resolve = function (val) {
6056 return new api(function (resolve, reject) {
6057 resolve(val);
6058 });
6059};
6060
6061api.reject = function (val) {
6062 return new api(function (resolve, reject) {
6063 reject(val);
6064 });
6065};
6066
6067var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6068
6069var Animation = function Animation(target, opts, opts2) {
6070 var isCore = core(target);
6071 var isEle = !isCore;
6072
6073 var _p = this._private = extend({
6074 duration: 1000
6075 }, opts, opts2);
6076
6077 _p.target = target;
6078 _p.style = _p.style || _p.css;
6079 _p.started = false;
6080 _p.playing = false;
6081 _p.hooked = false;
6082 _p.applying = false;
6083 _p.progress = 0;
6084 _p.completes = [];
6085 _p.frames = [];
6086
6087 if (_p.complete && fn(_p.complete)) {
6088 _p.completes.push(_p.complete);
6089 }
6090
6091 if (isEle) {
6092 var pos = target.position();
6093 _p.startPosition = _p.startPosition || {
6094 x: pos.x,
6095 y: pos.y
6096 };
6097 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6098 }
6099
6100 if (isCore) {
6101 var pan = target.pan();
6102 _p.startPan = {
6103 x: pan.x,
6104 y: pan.y
6105 };
6106 _p.startZoom = target.zoom();
6107 } // for future timeline/animations impl
6108
6109
6110 this.length = 1;
6111 this[0] = this;
6112};
6113
6114var anifn = Animation.prototype;
6115extend(anifn, {
6116 instanceString: function instanceString() {
6117 return 'animation';
6118 },
6119 hook: function hook() {
6120 var _p = this._private;
6121
6122 if (!_p.hooked) {
6123 // add to target's animation queue
6124 var q;
6125 var tAni = _p.target._private.animation;
6126
6127 if (_p.queue) {
6128 q = tAni.queue;
6129 } else {
6130 q = tAni.current;
6131 }
6132
6133 q.push(this); // add to the animation loop pool
6134
6135 if (elementOrCollection(_p.target)) {
6136 _p.target.cy().addToAnimationPool(_p.target);
6137 }
6138
6139 _p.hooked = true;
6140 }
6141
6142 return this;
6143 },
6144 play: function play() {
6145 var _p = this._private; // autorewind
6146
6147 if (_p.progress === 1) {
6148 _p.progress = 0;
6149 }
6150
6151 _p.playing = true;
6152 _p.started = false; // needs to be started by animation loop
6153
6154 _p.stopped = false;
6155 this.hook(); // the animation loop will start the animation...
6156
6157 return this;
6158 },
6159 playing: function playing() {
6160 return this._private.playing;
6161 },
6162 apply: function apply() {
6163 var _p = this._private;
6164 _p.applying = true;
6165 _p.started = false; // needs to be started by animation loop
6166
6167 _p.stopped = false;
6168 this.hook(); // the animation loop will apply the animation at this progress
6169
6170 return this;
6171 },
6172 applying: function applying() {
6173 return this._private.applying;
6174 },
6175 pause: function pause() {
6176 var _p = this._private;
6177 _p.playing = false;
6178 _p.started = false;
6179 return this;
6180 },
6181 stop: function stop() {
6182 var _p = this._private;
6183 _p.playing = false;
6184 _p.started = false;
6185 _p.stopped = true; // to be removed from animation queues
6186
6187 return this;
6188 },
6189 rewind: function rewind() {
6190 return this.progress(0);
6191 },
6192 fastforward: function fastforward() {
6193 return this.progress(1);
6194 },
6195 time: function time(t) {
6196 var _p = this._private;
6197
6198 if (t === undefined) {
6199 return _p.progress * _p.duration;
6200 } else {
6201 return this.progress(t / _p.duration);
6202 }
6203 },
6204 progress: function progress(p) {
6205 var _p = this._private;
6206 var wasPlaying = _p.playing;
6207
6208 if (p === undefined) {
6209 return _p.progress;
6210 } else {
6211 if (wasPlaying) {
6212 this.pause();
6213 }
6214
6215 _p.progress = p;
6216 _p.started = false;
6217
6218 if (wasPlaying) {
6219 this.play();
6220 }
6221 }
6222
6223 return this;
6224 },
6225 completed: function completed() {
6226 return this._private.progress === 1;
6227 },
6228 reverse: function reverse() {
6229 var _p = this._private;
6230 var wasPlaying = _p.playing;
6231
6232 if (wasPlaying) {
6233 this.pause();
6234 }
6235
6236 _p.progress = 1 - _p.progress;
6237 _p.started = false;
6238
6239 var swap = function swap(a, b) {
6240 var _pa = _p[a];
6241
6242 if (_pa == null) {
6243 return;
6244 }
6245
6246 _p[a] = _p[b];
6247 _p[b] = _pa;
6248 };
6249
6250 swap('zoom', 'startZoom');
6251 swap('pan', 'startPan');
6252 swap('position', 'startPosition'); // swap styles
6253
6254 if (_p.style) {
6255 for (var i = 0; i < _p.style.length; i++) {
6256 var prop = _p.style[i];
6257 var name = prop.name;
6258 var startStyleProp = _p.startStyle[name];
6259 _p.startStyle[name] = prop;
6260 _p.style[i] = startStyleProp;
6261 }
6262 }
6263
6264 if (wasPlaying) {
6265 this.play();
6266 }
6267
6268 return this;
6269 },
6270 promise: function promise(type) {
6271 var _p = this._private;
6272 var arr;
6273
6274 switch (type) {
6275 case 'frame':
6276 arr = _p.frames;
6277 break;
6278
6279 default:
6280 case 'complete':
6281 case 'completed':
6282 arr = _p.completes;
6283 }
6284
6285 return new Promise$1(function (resolve, reject) {
6286 arr.push(function () {
6287 resolve();
6288 });
6289 });
6290 }
6291});
6292anifn.complete = anifn.completed;
6293anifn.run = anifn.play;
6294anifn.running = anifn.playing;
6295
6296var define = {
6297 animated: function animated() {
6298 return function animatedImpl() {
6299 var self = this;
6300 var selfIsArrayLike = self.length !== undefined;
6301 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6302
6303 var cy = this._private.cy || this;
6304
6305 if (!cy.styleEnabled()) {
6306 return false;
6307 }
6308
6309 var ele = all[0];
6310
6311 if (ele) {
6312 return ele._private.animation.current.length > 0;
6313 }
6314 };
6315 },
6316 // animated
6317 clearQueue: function clearQueue() {
6318 return function clearQueueImpl() {
6319 var self = this;
6320 var selfIsArrayLike = self.length !== undefined;
6321 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6322
6323 var cy = this._private.cy || this;
6324
6325 if (!cy.styleEnabled()) {
6326 return this;
6327 }
6328
6329 for (var i = 0; i < all.length; i++) {
6330 var ele = all[i];
6331 ele._private.animation.queue = [];
6332 }
6333
6334 return this;
6335 };
6336 },
6337 // clearQueue
6338 delay: function delay() {
6339 return function delayImpl(time, complete) {
6340 var cy = this._private.cy || this;
6341
6342 if (!cy.styleEnabled()) {
6343 return this;
6344 }
6345
6346 return this.animate({
6347 delay: time,
6348 duration: time,
6349 complete: complete
6350 });
6351 };
6352 },
6353 // delay
6354 delayAnimation: function delayAnimation() {
6355 return function delayAnimationImpl(time, complete) {
6356 var cy = this._private.cy || this;
6357
6358 if (!cy.styleEnabled()) {
6359 return this;
6360 }
6361
6362 return this.animation({
6363 delay: time,
6364 duration: time,
6365 complete: complete
6366 });
6367 };
6368 },
6369 // delay
6370 animation: function animation() {
6371 return function animationImpl(properties, params) {
6372 var self = this;
6373 var selfIsArrayLike = self.length !== undefined;
6374 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6375
6376 var cy = this._private.cy || this;
6377 var isCore = !selfIsArrayLike;
6378 var isEles = !isCore;
6379
6380 if (!cy.styleEnabled()) {
6381 return this;
6382 }
6383
6384 var style = cy.style();
6385 properties = extend({}, properties, params);
6386 var propertiesEmpty = Object.keys(properties).length === 0;
6387
6388 if (propertiesEmpty) {
6389 return new Animation(all[0], properties); // nothing to animate
6390 }
6391
6392 if (properties.duration === undefined) {
6393 properties.duration = 400;
6394 }
6395
6396 switch (properties.duration) {
6397 case 'slow':
6398 properties.duration = 600;
6399 break;
6400
6401 case 'fast':
6402 properties.duration = 200;
6403 break;
6404 }
6405
6406 if (isEles) {
6407 properties.style = style.getPropsList(properties.style || properties.css);
6408 properties.css = undefined;
6409 }
6410
6411 if (isEles && properties.renderedPosition != null) {
6412 var rpos = properties.renderedPosition;
6413 var pan = cy.pan();
6414 var zoom = cy.zoom();
6415 properties.position = renderedToModelPosition(rpos, zoom, pan);
6416 } // override pan w/ panBy if set
6417
6418
6419 if (isCore && properties.panBy != null) {
6420 var panBy = properties.panBy;
6421 var cyPan = cy.pan();
6422 properties.pan = {
6423 x: cyPan.x + panBy.x,
6424 y: cyPan.y + panBy.y
6425 };
6426 } // override pan w/ center if set
6427
6428
6429 var center = properties.center || properties.centre;
6430
6431 if (isCore && center != null) {
6432 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6433
6434 if (centerPan != null) {
6435 properties.pan = centerPan;
6436 }
6437 } // override pan & zoom w/ fit if set
6438
6439
6440 if (isCore && properties.fit != null) {
6441 var fit = properties.fit;
6442 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6443
6444 if (fitVp != null) {
6445 properties.pan = fitVp.pan;
6446 properties.zoom = fitVp.zoom;
6447 }
6448 } // override zoom (& potentially pan) w/ zoom obj if set
6449
6450
6451 if (isCore && plainObject(properties.zoom)) {
6452 var vp = cy.getZoomedViewport(properties.zoom);
6453
6454 if (vp != null) {
6455 if (vp.zoomed) {
6456 properties.zoom = vp.zoom;
6457 }
6458
6459 if (vp.panned) {
6460 properties.pan = vp.pan;
6461 }
6462 } else {
6463 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6464 }
6465 }
6466
6467 return new Animation(all[0], properties);
6468 };
6469 },
6470 // animate
6471 animate: function animate() {
6472 return function animateImpl(properties, params) {
6473 var self = this;
6474 var selfIsArrayLike = self.length !== undefined;
6475 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6476
6477 var cy = this._private.cy || this;
6478
6479 if (!cy.styleEnabled()) {
6480 return this;
6481 }
6482
6483 if (params) {
6484 properties = extend({}, properties, params);
6485 } // manually hook and run the animation
6486
6487
6488 for (var i = 0; i < all.length; i++) {
6489 var ele = all[i];
6490 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6491 var ani = ele.animation(properties, queue ? {
6492 queue: true
6493 } : undefined);
6494 ani.play();
6495 }
6496
6497 return this; // chaining
6498 };
6499 },
6500 // animate
6501 stop: function stop() {
6502 return function stopImpl(clearQueue, jumpToEnd) {
6503 var self = this;
6504 var selfIsArrayLike = self.length !== undefined;
6505 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6506
6507 var cy = this._private.cy || this;
6508
6509 if (!cy.styleEnabled()) {
6510 return this;
6511 }
6512
6513 for (var i = 0; i < all.length; i++) {
6514 var ele = all[i];
6515 var _p = ele._private;
6516 var anis = _p.animation.current;
6517
6518 for (var j = 0; j < anis.length; j++) {
6519 var ani = anis[j];
6520 var ani_p = ani._private;
6521
6522 if (jumpToEnd) {
6523 // next iteration of the animation loop, the animation
6524 // will go straight to the end and be removed
6525 ani_p.duration = 0;
6526 }
6527 } // clear the queue of future animations
6528
6529
6530 if (clearQueue) {
6531 _p.animation.queue = [];
6532 }
6533
6534 if (!jumpToEnd) {
6535 _p.animation.current = [];
6536 }
6537 } // we have to notify (the animation loop doesn't do it for us on `stop`)
6538
6539
6540 cy.notify('draw');
6541 return this;
6542 };
6543 } // stop
6544
6545}; // define
6546
6547var define$1 = {
6548 // access data field
6549 data: function data(params) {
6550 var defaults = {
6551 field: 'data',
6552 bindingEvent: 'data',
6553 allowBinding: false,
6554 allowSetting: false,
6555 allowGetting: false,
6556 settingEvent: 'data',
6557 settingTriggersEvent: false,
6558 triggerFnName: 'trigger',
6559 immutableKeys: {},
6560 // key => true if immutable
6561 updateStyle: false,
6562 beforeGet: function beforeGet(self) {},
6563 beforeSet: function beforeSet(self, obj) {},
6564 onSet: function onSet(self) {},
6565 canSet: function canSet(self) {
6566 return true;
6567 }
6568 };
6569 params = extend({}, defaults, params);
6570 return function dataImpl(name, value) {
6571 var p = params;
6572 var self = this;
6573 var selfIsArrayLike = self.length !== undefined;
6574 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6575
6576 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
6577
6578 if (string(name)) {
6579 // set or get property
6580 // .data('foo')
6581 if (p.allowGetting && value === undefined) {
6582 // get
6583 var ret;
6584
6585 if (single) {
6586 p.beforeGet(single);
6587 ret = single._private[p.field][name];
6588 }
6589
6590 return ret; // .data('foo', 'bar')
6591 } else if (p.allowSetting && value !== undefined) {
6592 // set
6593 var valid = !p.immutableKeys[name];
6594
6595 if (valid) {
6596 var change = _defineProperty({}, name, value);
6597
6598 p.beforeSet(self, change);
6599
6600 for (var i = 0, l = all.length; i < l; i++) {
6601 var ele = all[i];
6602
6603 if (p.canSet(ele)) {
6604 ele._private[p.field][name] = value;
6605 }
6606 } // update mappers if asked
6607
6608
6609 if (p.updateStyle) {
6610 self.updateStyle();
6611 } // call onSet callback
6612
6613
6614 p.onSet(self);
6615
6616 if (p.settingTriggersEvent) {
6617 self[p.triggerFnName](p.settingEvent);
6618 }
6619 }
6620 } // .data({ 'foo': 'bar' })
6621
6622 } else if (p.allowSetting && plainObject(name)) {
6623 // extend
6624 var obj = name;
6625 var k, v;
6626 var keys = Object.keys(obj);
6627 p.beforeSet(self, obj);
6628
6629 for (var _i = 0; _i < keys.length; _i++) {
6630 k = keys[_i];
6631 v = obj[k];
6632
6633 var _valid = !p.immutableKeys[k];
6634
6635 if (_valid) {
6636 for (var j = 0; j < all.length; j++) {
6637 var _ele = all[j];
6638
6639 if (p.canSet(_ele)) {
6640 _ele._private[p.field][k] = v;
6641 }
6642 }
6643 }
6644 } // update mappers if asked
6645
6646
6647 if (p.updateStyle) {
6648 self.updateStyle();
6649 } // call onSet callback
6650
6651
6652 p.onSet(self);
6653
6654 if (p.settingTriggersEvent) {
6655 self[p.triggerFnName](p.settingEvent);
6656 } // .data(function(){ ... })
6657
6658 } else if (p.allowBinding && fn(name)) {
6659 // bind to event
6660 var fn$1 = name;
6661 self.on(p.bindingEvent, fn$1); // .data()
6662 } else if (p.allowGetting && name === undefined) {
6663 // get whole object
6664 var _ret;
6665
6666 if (single) {
6667 p.beforeGet(single);
6668 _ret = single._private[p.field];
6669 }
6670
6671 return _ret;
6672 }
6673
6674 return self; // maintain chainability
6675 }; // function
6676 },
6677 // data
6678 // remove data field
6679 removeData: function removeData(params) {
6680 var defaults = {
6681 field: 'data',
6682 event: 'data',
6683 triggerFnName: 'trigger',
6684 triggerEvent: false,
6685 immutableKeys: {} // key => true if immutable
6686
6687 };
6688 params = extend({}, defaults, params);
6689 return function removeDataImpl(names) {
6690 var p = params;
6691 var self = this;
6692 var selfIsArrayLike = self.length !== undefined;
6693 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6694 // .removeData('foo bar')
6695
6696 if (string(names)) {
6697 // then get the list of keys, and delete them
6698 var keys = names.split(/\s+/);
6699 var l = keys.length;
6700
6701 for (var i = 0; i < l; i++) {
6702 // delete each non-empty key
6703 var key = keys[i];
6704
6705 if (emptyString(key)) {
6706 continue;
6707 }
6708
6709 var valid = !p.immutableKeys[key]; // not valid if immutable
6710
6711 if (valid) {
6712 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
6713 all[i_a]._private[p.field][key] = undefined;
6714 }
6715 }
6716 }
6717
6718 if (p.triggerEvent) {
6719 self[p.triggerFnName](p.event);
6720 } // .removeData()
6721
6722 } else if (names === undefined) {
6723 // then delete all keys
6724 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
6725 var _privateFields = all[_i_a]._private[p.field];
6726
6727 var _keys = Object.keys(_privateFields);
6728
6729 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
6730 var _key = _keys[_i2];
6731 var validKeyToDelete = !p.immutableKeys[_key];
6732
6733 if (validKeyToDelete) {
6734 _privateFields[_key] = undefined;
6735 }
6736 }
6737 }
6738
6739 if (p.triggerEvent) {
6740 self[p.triggerFnName](p.event);
6741 }
6742 }
6743
6744 return self; // maintain chaining
6745 }; // function
6746 } // removeData
6747
6748}; // define
6749
6750var define$2 = {
6751 eventAliasesOn: function eventAliasesOn(proto) {
6752 var p = proto;
6753 p.addListener = p.listen = p.bind = p.on;
6754 p.unlisten = p.unbind = p.off = p.removeListener;
6755 p.trigger = p.emit; // this is just a wrapper alias of .on()
6756
6757 p.pon = p.promiseOn = function (events, selector) {
6758 var self = this;
6759 var args = Array.prototype.slice.call(arguments, 0);
6760 return new Promise$1(function (resolve, reject) {
6761 var callback = function callback(e) {
6762 self.off.apply(self, offArgs);
6763 resolve(e);
6764 };
6765
6766 var onArgs = args.concat([callback]);
6767 var offArgs = onArgs.concat([]);
6768 self.on.apply(self, onArgs);
6769 });
6770 };
6771 }
6772}; // define
6773
6774// use this module to cherry pick functions into your prototype
6775var define$3 = {};
6776[define, define$1, define$2].forEach(function (m) {
6777 extend(define$3, m);
6778});
6779
6780var elesfn$d = {
6781 animate: define$3.animate(),
6782 animation: define$3.animation(),
6783 animated: define$3.animated(),
6784 clearQueue: define$3.clearQueue(),
6785 delay: define$3.delay(),
6786 delayAnimation: define$3.delayAnimation(),
6787 stop: define$3.stop()
6788};
6789
6790var elesfn$e = {
6791 classes: function classes(_classes) {
6792 var self = this;
6793
6794 if (_classes === undefined) {
6795 var ret = [];
6796
6797 self[0]._private.classes.forEach(function (cls) {
6798 return ret.push(cls);
6799 });
6800
6801 return ret;
6802 } else if (!array(_classes)) {
6803 // extract classes from string
6804 _classes = (_classes || '').match(/\S+/g) || [];
6805 }
6806
6807 var changed = [];
6808 var classesSet = new Set$1(_classes); // check and update each ele
6809
6810 for (var j = 0; j < self.length; j++) {
6811 var ele = self[j];
6812 var _p = ele._private;
6813 var eleClasses = _p.classes;
6814 var changedEle = false; // check if ele has all of the passed classes
6815
6816 for (var i = 0; i < _classes.length; i++) {
6817 var cls = _classes[i];
6818 var eleHasClass = eleClasses.has(cls);
6819
6820 if (!eleHasClass) {
6821 changedEle = true;
6822 break;
6823 }
6824 } // check if ele has classes outside of those passed
6825
6826
6827 if (!changedEle) {
6828 changedEle = eleClasses.size !== _classes.length;
6829 }
6830
6831 if (changedEle) {
6832 _p.classes = classesSet;
6833 changed.push(ele);
6834 }
6835 } // trigger update style on those eles that had class changes
6836
6837
6838 if (changed.length > 0) {
6839 this.spawn(changed).updateStyle().emit('class');
6840 }
6841
6842 return self;
6843 },
6844 addClass: function addClass(classes) {
6845 return this.toggleClass(classes, true);
6846 },
6847 hasClass: function hasClass(className) {
6848 var ele = this[0];
6849 return ele != null && ele._private.classes.has(className);
6850 },
6851 toggleClass: function toggleClass(classes, toggle) {
6852 if (!array(classes)) {
6853 // extract classes from string
6854 classes = classes.match(/\S+/g) || [];
6855 }
6856
6857 var self = this;
6858 var toggleUndefd = toggle === undefined;
6859 var changed = []; // eles who had classes changed
6860
6861 for (var i = 0, il = self.length; i < il; i++) {
6862 var ele = self[i];
6863 var eleClasses = ele._private.classes;
6864 var changedEle = false;
6865
6866 for (var j = 0; j < classes.length; j++) {
6867 var cls = classes[j];
6868 var hasClass = eleClasses.has(cls);
6869 var changedNow = false;
6870
6871 if (toggle || toggleUndefd && !hasClass) {
6872 eleClasses.add(cls);
6873 changedNow = true;
6874 } else if (!toggle || toggleUndefd && hasClass) {
6875 eleClasses["delete"](cls);
6876 changedNow = true;
6877 }
6878
6879 if (!changedEle && changedNow) {
6880 changed.push(ele);
6881 changedEle = true;
6882 }
6883 } // for j classes
6884
6885 } // for i eles
6886 // trigger update style on those eles that had class changes
6887
6888
6889 if (changed.length > 0) {
6890 this.spawn(changed).updateStyle().emit('class');
6891 }
6892
6893 return self;
6894 },
6895 removeClass: function removeClass(classes) {
6896 return this.toggleClass(classes, false);
6897 },
6898 flashClass: function flashClass(classes, duration) {
6899 var self = this;
6900
6901 if (duration == null) {
6902 duration = 250;
6903 } else if (duration === 0) {
6904 return self; // nothing to do really
6905 }
6906
6907 self.addClass(classes);
6908 setTimeout(function () {
6909 self.removeClass(classes);
6910 }, duration);
6911 return self;
6912 }
6913};
6914elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
6915
6916var tokens = {
6917 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
6918 // chars we need to escape in let names, etc
6919 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
6920 // binary comparison op (used in data selectors)
6921 boolOp: '\\?|\\!|\\^',
6922 // boolean (unary) operators (used in data selectors)
6923 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
6924 // string literals (used in data selectors) -- doublequotes | singlequotes
6925 number: number$1,
6926 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
6927 meta: 'degree|indegree|outdegree',
6928 // allowed metadata fields (i.e. allowed functions to use from Collection)
6929 separator: '\\s*,\\s*',
6930 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
6931 descendant: '\\s+',
6932 child: '\\s+>\\s+',
6933 subject: '\\$',
6934 group: 'node|edge|\\*',
6935 directedEdge: '\\s+->\\s+',
6936 undirectedEdge: '\\s+<->\\s+'
6937};
6938tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
6939
6940tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
6941
6942tokens.className = tokens.variable; // a class name (follows variable conventions)
6943
6944tokens.id = tokens.variable; // an element id (follows variable conventions)
6945
6946(function () {
6947 var ops, op, i; // add @ variants to comparatorOp
6948
6949 ops = tokens.comparatorOp.split('|');
6950
6951 for (i = 0; i < ops.length; i++) {
6952 op = ops[i];
6953 tokens.comparatorOp += '|@' + op;
6954 } // add ! variants to comparatorOp
6955
6956
6957 ops = tokens.comparatorOp.split('|');
6958
6959 for (i = 0; i < ops.length; i++) {
6960 op = ops[i];
6961
6962 if (op.indexOf('!') >= 0) {
6963 continue;
6964 } // skip ops that explicitly contain !
6965
6966
6967 if (op === '=') {
6968 continue;
6969 } // skip = b/c != is explicitly defined
6970
6971
6972 tokens.comparatorOp += '|\\!' + op;
6973 }
6974})();
6975
6976/**
6977 * Make a new query object
6978 *
6979 * @prop type {Type} The type enum (int) of the query
6980 * @prop checks List of checks to make against an ele to test for a match
6981 */
6982var newQuery = function newQuery() {
6983 return {
6984 checks: []
6985 };
6986};
6987
6988/**
6989 * A check type enum-like object. Uses integer values for fast match() lookup.
6990 * The ordering does not matter as long as the ints are unique.
6991 */
6992var Type = {
6993 /** E.g. node */
6994 GROUP: 0,
6995
6996 /** A collection of elements */
6997 COLLECTION: 1,
6998
6999 /** A filter(ele) function */
7000 FILTER: 2,
7001
7002 /** E.g. [foo > 1] */
7003 DATA_COMPARE: 3,
7004
7005 /** E.g. [foo] */
7006 DATA_EXIST: 4,
7007
7008 /** E.g. [?foo] */
7009 DATA_BOOL: 5,
7010
7011 /** E.g. [[degree > 2]] */
7012 META_COMPARE: 6,
7013
7014 /** E.g. :selected */
7015 STATE: 7,
7016
7017 /** E.g. #foo */
7018 ID: 8,
7019
7020 /** E.g. .foo */
7021 CLASS: 9,
7022
7023 /** E.g. #foo <-> #bar */
7024 UNDIRECTED_EDGE: 10,
7025
7026 /** E.g. #foo -> #bar */
7027 DIRECTED_EDGE: 11,
7028
7029 /** E.g. $#foo -> #bar */
7030 NODE_SOURCE: 12,
7031
7032 /** E.g. #foo -> $#bar */
7033 NODE_TARGET: 13,
7034
7035 /** E.g. $#foo <-> #bar */
7036 NODE_NEIGHBOR: 14,
7037
7038 /** E.g. #foo > #bar */
7039 CHILD: 15,
7040
7041 /** E.g. #foo #bar */
7042 DESCENDANT: 16,
7043
7044 /** E.g. $#foo > #bar */
7045 PARENT: 17,
7046
7047 /** E.g. $#foo #bar */
7048 ANCESTOR: 18,
7049
7050 /** E.g. #foo > $bar > #baz */
7051 COMPOUND_SPLIT: 19,
7052
7053 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7054 TRUE: 20
7055};
7056
7057var stateSelectors = [{
7058 selector: ':selected',
7059 matches: function matches(ele) {
7060 return ele.selected();
7061 }
7062}, {
7063 selector: ':unselected',
7064 matches: function matches(ele) {
7065 return !ele.selected();
7066 }
7067}, {
7068 selector: ':selectable',
7069 matches: function matches(ele) {
7070 return ele.selectable();
7071 }
7072}, {
7073 selector: ':unselectable',
7074 matches: function matches(ele) {
7075 return !ele.selectable();
7076 }
7077}, {
7078 selector: ':locked',
7079 matches: function matches(ele) {
7080 return ele.locked();
7081 }
7082}, {
7083 selector: ':unlocked',
7084 matches: function matches(ele) {
7085 return !ele.locked();
7086 }
7087}, {
7088 selector: ':visible',
7089 matches: function matches(ele) {
7090 return ele.visible();
7091 }
7092}, {
7093 selector: ':hidden',
7094 matches: function matches(ele) {
7095 return !ele.visible();
7096 }
7097}, {
7098 selector: ':transparent',
7099 matches: function matches(ele) {
7100 return ele.transparent();
7101 }
7102}, {
7103 selector: ':grabbed',
7104 matches: function matches(ele) {
7105 return ele.grabbed();
7106 }
7107}, {
7108 selector: ':free',
7109 matches: function matches(ele) {
7110 return !ele.grabbed();
7111 }
7112}, {
7113 selector: ':removed',
7114 matches: function matches(ele) {
7115 return ele.removed();
7116 }
7117}, {
7118 selector: ':inside',
7119 matches: function matches(ele) {
7120 return !ele.removed();
7121 }
7122}, {
7123 selector: ':grabbable',
7124 matches: function matches(ele) {
7125 return ele.grabbable();
7126 }
7127}, {
7128 selector: ':ungrabbable',
7129 matches: function matches(ele) {
7130 return !ele.grabbable();
7131 }
7132}, {
7133 selector: ':animated',
7134 matches: function matches(ele) {
7135 return ele.animated();
7136 }
7137}, {
7138 selector: ':unanimated',
7139 matches: function matches(ele) {
7140 return !ele.animated();
7141 }
7142}, {
7143 selector: ':parent',
7144 matches: function matches(ele) {
7145 return ele.isParent();
7146 }
7147}, {
7148 selector: ':childless',
7149 matches: function matches(ele) {
7150 return ele.isChildless();
7151 }
7152}, {
7153 selector: ':child',
7154 matches: function matches(ele) {
7155 return ele.isChild();
7156 }
7157}, {
7158 selector: ':orphan',
7159 matches: function matches(ele) {
7160 return ele.isOrphan();
7161 }
7162}, {
7163 selector: ':nonorphan',
7164 matches: function matches(ele) {
7165 return ele.isChild();
7166 }
7167}, {
7168 selector: ':compound',
7169 matches: function matches(ele) {
7170 if (ele.isNode()) {
7171 return ele.isParent();
7172 } else {
7173 return ele.source().isParent() || ele.target().isParent();
7174 }
7175 }
7176}, {
7177 selector: ':loop',
7178 matches: function matches(ele) {
7179 return ele.isLoop();
7180 }
7181}, {
7182 selector: ':simple',
7183 matches: function matches(ele) {
7184 return ele.isSimple();
7185 }
7186}, {
7187 selector: ':active',
7188 matches: function matches(ele) {
7189 return ele.active();
7190 }
7191}, {
7192 selector: ':inactive',
7193 matches: function matches(ele) {
7194 return !ele.active();
7195 }
7196}, {
7197 selector: ':backgrounding',
7198 matches: function matches(ele) {
7199 return ele.backgrounding();
7200 }
7201}, {
7202 selector: ':nonbackgrounding',
7203 matches: function matches(ele) {
7204 return !ele.backgrounding();
7205 }
7206}].sort(function (a, b) {
7207 // n.b. selectors that are starting substrings of others must have the longer ones first
7208 return descending(a.selector, b.selector);
7209});
7210
7211var lookup = function () {
7212 var selToFn = {};
7213 var s;
7214
7215 for (var i = 0; i < stateSelectors.length; i++) {
7216 s = stateSelectors[i];
7217 selToFn[s.selector] = s.matches;
7218 }
7219
7220 return selToFn;
7221}();
7222
7223var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7224 return lookup[sel](ele);
7225};
7226var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7227 return s.selector;
7228}).join('|') + ')';
7229
7230// so that values get compared properly in Selector.filter()
7231
7232var cleanMetaChars = function cleanMetaChars(str) {
7233 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7234 return $1;
7235 });
7236};
7237
7238var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7239 selector[selector.length - 1] = replacementQuery;
7240}; // NOTE: add new expression syntax here to have it recognised by the parser;
7241// - a query contains all adjacent (i.e. no separator in between) expressions;
7242// - the current query is stored in selector[i]
7243// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7244
7245
7246var exprs = [{
7247 name: 'group',
7248 // just used for identifying when debugging
7249 query: true,
7250 regex: '(' + tokens.group + ')',
7251 populate: function populate(selector, query, _ref) {
7252 var _ref2 = _slicedToArray(_ref, 1),
7253 group = _ref2[0];
7254
7255 query.checks.push({
7256 type: Type.GROUP,
7257 value: group === '*' ? group : group + 's'
7258 });
7259 }
7260}, {
7261 name: 'state',
7262 query: true,
7263 regex: stateSelectorRegex,
7264 populate: function populate(selector, query, _ref3) {
7265 var _ref4 = _slicedToArray(_ref3, 1),
7266 state = _ref4[0];
7267
7268 query.checks.push({
7269 type: Type.STATE,
7270 value: state
7271 });
7272 }
7273}, {
7274 name: 'id',
7275 query: true,
7276 regex: '\\#(' + tokens.id + ')',
7277 populate: function populate(selector, query, _ref5) {
7278 var _ref6 = _slicedToArray(_ref5, 1),
7279 id = _ref6[0];
7280
7281 query.checks.push({
7282 type: Type.ID,
7283 value: cleanMetaChars(id)
7284 });
7285 }
7286}, {
7287 name: 'className',
7288 query: true,
7289 regex: '\\.(' + tokens.className + ')',
7290 populate: function populate(selector, query, _ref7) {
7291 var _ref8 = _slicedToArray(_ref7, 1),
7292 className = _ref8[0];
7293
7294 query.checks.push({
7295 type: Type.CLASS,
7296 value: cleanMetaChars(className)
7297 });
7298 }
7299}, {
7300 name: 'dataExists',
7301 query: true,
7302 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
7303 populate: function populate(selector, query, _ref9) {
7304 var _ref10 = _slicedToArray(_ref9, 1),
7305 variable = _ref10[0];
7306
7307 query.checks.push({
7308 type: Type.DATA_EXIST,
7309 field: cleanMetaChars(variable)
7310 });
7311 }
7312}, {
7313 name: 'dataCompare',
7314 query: true,
7315 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
7316 populate: function populate(selector, query, _ref11) {
7317 var _ref12 = _slicedToArray(_ref11, 3),
7318 variable = _ref12[0],
7319 comparatorOp = _ref12[1],
7320 value = _ref12[2];
7321
7322 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
7323
7324 if (valueIsString) {
7325 value = value.substring(1, value.length - 1);
7326 } else {
7327 value = parseFloat(value);
7328 }
7329
7330 query.checks.push({
7331 type: Type.DATA_COMPARE,
7332 field: cleanMetaChars(variable),
7333 operator: comparatorOp,
7334 value: value
7335 });
7336 }
7337}, {
7338 name: 'dataBool',
7339 query: true,
7340 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
7341 populate: function populate(selector, query, _ref13) {
7342 var _ref14 = _slicedToArray(_ref13, 2),
7343 boolOp = _ref14[0],
7344 variable = _ref14[1];
7345
7346 query.checks.push({
7347 type: Type.DATA_BOOL,
7348 field: cleanMetaChars(variable),
7349 operator: boolOp
7350 });
7351 }
7352}, {
7353 name: 'metaCompare',
7354 query: true,
7355 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
7356 populate: function populate(selector, query, _ref15) {
7357 var _ref16 = _slicedToArray(_ref15, 3),
7358 meta = _ref16[0],
7359 comparatorOp = _ref16[1],
7360 number = _ref16[2];
7361
7362 query.checks.push({
7363 type: Type.META_COMPARE,
7364 field: cleanMetaChars(meta),
7365 operator: comparatorOp,
7366 value: parseFloat(number)
7367 });
7368 }
7369}, {
7370 name: 'nextQuery',
7371 separator: true,
7372 regex: tokens.separator,
7373 populate: function populate(selector, query) {
7374 var currentSubject = selector.currentSubject;
7375 var edgeCount = selector.edgeCount;
7376 var compoundCount = selector.compoundCount;
7377 var lastQ = selector[selector.length - 1];
7378
7379 if (currentSubject != null) {
7380 lastQ.subject = currentSubject;
7381 selector.currentSubject = null;
7382 }
7383
7384 lastQ.edgeCount = edgeCount;
7385 lastQ.compoundCount = compoundCount;
7386 selector.edgeCount = 0;
7387 selector.compoundCount = 0; // go on to next query
7388
7389 var nextQuery = selector[selector.length++] = newQuery();
7390 return nextQuery; // this is the new query to be filled by the following exprs
7391 }
7392}, {
7393 name: 'directedEdge',
7394 separator: true,
7395 regex: tokens.directedEdge,
7396 populate: function populate(selector, query) {
7397 if (selector.currentSubject == null) {
7398 // undirected edge
7399 var edgeQuery = newQuery();
7400 var source = query;
7401 var target = newQuery();
7402 edgeQuery.checks.push({
7403 type: Type.DIRECTED_EDGE,
7404 source: source,
7405 target: target
7406 }); // the query in the selector should be the edge rather than the source
7407
7408 replaceLastQuery(selector, query, edgeQuery);
7409 selector.edgeCount++; // we're now populating the target query with expressions that follow
7410
7411 return target;
7412 } else {
7413 // source/target
7414 var srcTgtQ = newQuery();
7415 var _source = query;
7416
7417 var _target = newQuery();
7418
7419 srcTgtQ.checks.push({
7420 type: Type.NODE_SOURCE,
7421 source: _source,
7422 target: _target
7423 }); // the query in the selector should be the neighbourhood rather than the node
7424
7425 replaceLastQuery(selector, query, srcTgtQ);
7426 selector.edgeCount++;
7427 return _target; // now populating the target with the following expressions
7428 }
7429 }
7430}, {
7431 name: 'undirectedEdge',
7432 separator: true,
7433 regex: tokens.undirectedEdge,
7434 populate: function populate(selector, query) {
7435 if (selector.currentSubject == null) {
7436 // undirected edge
7437 var edgeQuery = newQuery();
7438 var source = query;
7439 var target = newQuery();
7440 edgeQuery.checks.push({
7441 type: Type.UNDIRECTED_EDGE,
7442 nodes: [source, target]
7443 }); // the query in the selector should be the edge rather than the source
7444
7445 replaceLastQuery(selector, query, edgeQuery);
7446 selector.edgeCount++; // we're now populating the target query with expressions that follow
7447
7448 return target;
7449 } else {
7450 // neighbourhood
7451 var nhoodQ = newQuery();
7452 var node = query;
7453 var neighbor = newQuery();
7454 nhoodQ.checks.push({
7455 type: Type.NODE_NEIGHBOR,
7456 node: node,
7457 neighbor: neighbor
7458 }); // the query in the selector should be the neighbourhood rather than the node
7459
7460 replaceLastQuery(selector, query, nhoodQ);
7461 return neighbor; // now populating the neighbor with following expressions
7462 }
7463 }
7464}, {
7465 name: 'child',
7466 separator: true,
7467 regex: tokens.child,
7468 populate: function populate(selector, query) {
7469 if (selector.currentSubject == null) {
7470 // default: child query
7471 var parentChildQuery = newQuery();
7472 var child = newQuery();
7473 var parent = selector[selector.length - 1];
7474 parentChildQuery.checks.push({
7475 type: Type.CHILD,
7476 parent: parent,
7477 child: child
7478 }); // the query in the selector should be the '>' itself
7479
7480 replaceLastQuery(selector, query, parentChildQuery);
7481 selector.compoundCount++; // we're now populating the child query with expressions that follow
7482
7483 return child;
7484 } else if (selector.currentSubject === query) {
7485 // compound split query
7486 var compound = newQuery();
7487 var left = selector[selector.length - 1];
7488 var right = newQuery();
7489 var subject = newQuery();
7490
7491 var _child = newQuery();
7492
7493 var _parent = newQuery(); // set up the root compound q
7494
7495
7496 compound.checks.push({
7497 type: Type.COMPOUND_SPLIT,
7498 left: left,
7499 right: right,
7500 subject: subject
7501 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7502
7503 subject.checks = query.checks; // take the checks from the left
7504
7505 query.checks = [{
7506 type: Type.TRUE
7507 }]; // checks under left refs the subject implicitly
7508 // set up the right q
7509
7510 _parent.checks.push({
7511 type: Type.TRUE
7512 }); // parent implicitly refs the subject
7513
7514
7515 right.checks.push({
7516 type: Type.PARENT,
7517 // type is swapped on right side queries
7518 parent: _parent,
7519 child: _child // empty for now
7520
7521 });
7522 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7523
7524 selector.currentSubject = subject;
7525 selector.compoundCount++;
7526 return _child; // now populating the right side's child
7527 } else {
7528 // parent query
7529 // info for parent query
7530 var _parent2 = newQuery();
7531
7532 var _child2 = newQuery();
7533
7534 var pcQChecks = [{
7535 type: Type.PARENT,
7536 parent: _parent2,
7537 child: _child2
7538 }]; // the parent-child query takes the place of the query previously being populated
7539
7540 _parent2.checks = query.checks; // the previous query contains the checks for the parent
7541
7542 query.checks = pcQChecks; // pc query takes over
7543
7544 selector.compoundCount++;
7545 return _child2; // we're now populating the child
7546 }
7547 }
7548}, {
7549 name: 'descendant',
7550 separator: true,
7551 regex: tokens.descendant,
7552 populate: function populate(selector, query) {
7553 if (selector.currentSubject == null) {
7554 // default: descendant query
7555 var ancChQuery = newQuery();
7556 var descendant = newQuery();
7557 var ancestor = selector[selector.length - 1];
7558 ancChQuery.checks.push({
7559 type: Type.DESCENDANT,
7560 ancestor: ancestor,
7561 descendant: descendant
7562 }); // the query in the selector should be the '>' itself
7563
7564 replaceLastQuery(selector, query, ancChQuery);
7565 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
7566
7567 return descendant;
7568 } else if (selector.currentSubject === query) {
7569 // compound split query
7570 var compound = newQuery();
7571 var left = selector[selector.length - 1];
7572 var right = newQuery();
7573 var subject = newQuery();
7574
7575 var _descendant = newQuery();
7576
7577 var _ancestor = newQuery(); // set up the root compound q
7578
7579
7580 compound.checks.push({
7581 type: Type.COMPOUND_SPLIT,
7582 left: left,
7583 right: right,
7584 subject: subject
7585 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7586
7587 subject.checks = query.checks; // take the checks from the left
7588
7589 query.checks = [{
7590 type: Type.TRUE
7591 }]; // checks under left refs the subject implicitly
7592 // set up the right q
7593
7594 _ancestor.checks.push({
7595 type: Type.TRUE
7596 }); // ancestor implicitly refs the subject
7597
7598
7599 right.checks.push({
7600 type: Type.ANCESTOR,
7601 // type is swapped on right side queries
7602 ancestor: _ancestor,
7603 descendant: _descendant // empty for now
7604
7605 });
7606 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7607
7608 selector.currentSubject = subject;
7609 selector.compoundCount++;
7610 return _descendant; // now populating the right side's descendant
7611 } else {
7612 // ancestor query
7613 // info for parent query
7614 var _ancestor2 = newQuery();
7615
7616 var _descendant2 = newQuery();
7617
7618 var adQChecks = [{
7619 type: Type.ANCESTOR,
7620 ancestor: _ancestor2,
7621 descendant: _descendant2
7622 }]; // the parent-child query takes the place of the query previously being populated
7623
7624 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
7625
7626 query.checks = adQChecks; // pc query takes over
7627
7628 selector.compoundCount++;
7629 return _descendant2; // we're now populating the child
7630 }
7631 }
7632}, {
7633 name: 'subject',
7634 modifier: true,
7635 regex: tokens.subject,
7636 populate: function populate(selector, query) {
7637 if (selector.currentSubject != null && selector.currentSubject !== query) {
7638 warn('Redefinition of subject in selector `' + selector.toString() + '`');
7639 return false;
7640 }
7641
7642 selector.currentSubject = query;
7643 var topQ = selector[selector.length - 1];
7644 var topChk = topQ.checks[0];
7645 var topType = topChk == null ? null : topChk.type;
7646
7647 if (topType === Type.DIRECTED_EDGE) {
7648 // directed edge with subject on the target
7649 // change to target node check
7650 topChk.type = Type.NODE_TARGET;
7651 } else if (topType === Type.UNDIRECTED_EDGE) {
7652 // undirected edge with subject on the second node
7653 // change to neighbor check
7654 topChk.type = Type.NODE_NEIGHBOR;
7655 topChk.node = topChk.nodes[1]; // second node is subject
7656
7657 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
7658
7659 topChk.nodes = null;
7660 }
7661 }
7662}];
7663exprs.forEach(function (e) {
7664 return e.regexObj = new RegExp('^' + e.regex);
7665});
7666
7667/**
7668 * Of all the expressions, find the first match in the remaining text.
7669 * @param {string} remaining The remaining text to parse
7670 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
7671 */
7672
7673var consumeExpr = function consumeExpr(remaining) {
7674 var expr;
7675 var match;
7676 var name;
7677
7678 for (var j = 0; j < exprs.length; j++) {
7679 var e = exprs[j];
7680 var n = e.name;
7681 var m = remaining.match(e.regexObj);
7682
7683 if (m != null) {
7684 match = m;
7685 expr = e;
7686 name = n;
7687 var consumed = m[0];
7688 remaining = remaining.substring(consumed.length);
7689 break; // we've consumed one expr, so we can return now
7690 }
7691 }
7692
7693 return {
7694 expr: expr,
7695 match: match,
7696 name: name,
7697 remaining: remaining
7698 };
7699};
7700/**
7701 * Consume all the leading whitespace
7702 * @param {string} remaining The text to consume
7703 * @returns The text with the leading whitespace removed
7704 */
7705
7706
7707var consumeWhitespace = function consumeWhitespace(remaining) {
7708 var match = remaining.match(/^\s+/);
7709
7710 if (match) {
7711 var consumed = match[0];
7712 remaining = remaining.substring(consumed.length);
7713 }
7714
7715 return remaining;
7716};
7717/**
7718 * Parse the string and store the parsed representation in the Selector.
7719 * @param {string} selector The selector string
7720 * @returns `true` if the selector was successfully parsed, `false` otherwise
7721 */
7722
7723
7724var parse = function parse(selector) {
7725 var self = this;
7726 var remaining = self.inputText = selector;
7727 var currentQuery = self[0] = newQuery();
7728 self.length = 1;
7729 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
7730
7731 for (;;) {
7732 var exprInfo = consumeExpr(remaining);
7733
7734 if (exprInfo.expr == null) {
7735 warn('The selector `' + selector + '`is invalid');
7736 return false;
7737 } else {
7738 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
7739
7740 var ret = exprInfo.expr.populate(self, currentQuery, args);
7741
7742 if (ret === false) {
7743 return false; // exit if population failed
7744 } else if (ret != null) {
7745 currentQuery = ret; // change the current query to be filled if the expr specifies
7746 }
7747 }
7748
7749 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
7750
7751 if (remaining.match(/^\s*$/)) {
7752 break;
7753 }
7754 }
7755
7756 var lastQ = self[self.length - 1];
7757
7758 if (self.currentSubject != null) {
7759 lastQ.subject = self.currentSubject;
7760 }
7761
7762 lastQ.edgeCount = self.edgeCount;
7763 lastQ.compoundCount = self.compoundCount;
7764
7765 for (var i = 0; i < self.length; i++) {
7766 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
7767
7768 if (q.compoundCount > 0 && q.edgeCount > 0) {
7769 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
7770 return false;
7771 }
7772
7773 if (q.edgeCount > 1) {
7774 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
7775 return false;
7776 } else if (q.edgeCount === 1) {
7777 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.');
7778 }
7779 }
7780
7781 return true; // success
7782};
7783/**
7784 * Get the selector represented as a string. This value uses default formatting,
7785 * so things like spacing may differ from the input text passed to the constructor.
7786 * @returns {string} The selector string
7787 */
7788
7789
7790var toString = function toString() {
7791 if (this.toStringCache != null) {
7792 return this.toStringCache;
7793 }
7794
7795 var clean = function clean(obj) {
7796 if (obj == null) {
7797 return '';
7798 } else {
7799 return obj;
7800 }
7801 };
7802
7803 var cleanVal = function cleanVal(val) {
7804 if (string(val)) {
7805 return '"' + val + '"';
7806 } else {
7807 return clean(val);
7808 }
7809 };
7810
7811 var space = function space(val) {
7812 return ' ' + val + ' ';
7813 };
7814
7815 var checkToString = function checkToString(check, subject) {
7816 var type = check.type,
7817 value = check.value;
7818
7819 switch (type) {
7820 case Type.GROUP:
7821 {
7822 var group = clean(value);
7823 return group.substring(0, group.length - 1);
7824 }
7825
7826 case Type.DATA_COMPARE:
7827 {
7828 var field = check.field,
7829 operator = check.operator;
7830 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
7831 }
7832
7833 case Type.DATA_BOOL:
7834 {
7835 var _operator = check.operator,
7836 _field = check.field;
7837 return '[' + clean(_operator) + _field + ']';
7838 }
7839
7840 case Type.DATA_EXIST:
7841 {
7842 var _field2 = check.field;
7843 return '[' + _field2 + ']';
7844 }
7845
7846 case Type.META_COMPARE:
7847 {
7848 var _operator2 = check.operator,
7849 _field3 = check.field;
7850 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
7851 }
7852
7853 case Type.STATE:
7854 {
7855 return value;
7856 }
7857
7858 case Type.ID:
7859 {
7860 return '#' + value;
7861 }
7862
7863 case Type.CLASS:
7864 {
7865 return '.' + value;
7866 }
7867
7868 case Type.PARENT:
7869 case Type.CHILD:
7870 {
7871 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
7872 }
7873
7874 case Type.ANCESTOR:
7875 case Type.DESCENDANT:
7876 {
7877 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
7878 }
7879
7880 case Type.COMPOUND_SPLIT:
7881 {
7882 var lhs = queryToString(check.left, subject);
7883 var sub = queryToString(check.subject, subject);
7884 var rhs = queryToString(check.right, subject);
7885 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
7886 }
7887
7888 case Type.TRUE:
7889 {
7890 return '';
7891 }
7892 }
7893 };
7894
7895 var queryToString = function queryToString(query, subject) {
7896 return query.checks.reduce(function (str, chk, i) {
7897 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
7898 }, '');
7899 };
7900
7901 var str = '';
7902
7903 for (var i = 0; i < this.length; i++) {
7904 var query = this[i];
7905 str += queryToString(query, query.subject);
7906
7907 if (this.length > 1 && i < this.length - 1) {
7908 str += ', ';
7909 }
7910 }
7911
7912 this.toStringCache = str;
7913 return str;
7914};
7915var parse$1 = {
7916 parse: parse,
7917 toString: toString
7918};
7919
7920var valCmp = function valCmp(fieldVal, operator, value) {
7921 var matches;
7922 var isFieldStr = string(fieldVal);
7923 var isFieldNum = number(fieldVal);
7924 var isValStr = string(value);
7925 var fieldStr, valStr;
7926 var caseInsensitive = false;
7927 var notExpr = false;
7928 var isIneqCmp = false;
7929
7930 if (operator.indexOf('!') >= 0) {
7931 operator = operator.replace('!', '');
7932 notExpr = true;
7933 }
7934
7935 if (operator.indexOf('@') >= 0) {
7936 operator = operator.replace('@', '');
7937 caseInsensitive = true;
7938 }
7939
7940 if (isFieldStr || isValStr || caseInsensitive) {
7941 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
7942 valStr = '' + value;
7943 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
7944 // even if we're comparing numbers
7945
7946
7947 if (caseInsensitive) {
7948 fieldVal = fieldStr = fieldStr.toLowerCase();
7949 value = valStr = valStr.toLowerCase();
7950 }
7951
7952 switch (operator) {
7953 case '*=':
7954 matches = fieldStr.indexOf(valStr) >= 0;
7955 break;
7956
7957 case '$=':
7958 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
7959 break;
7960
7961 case '^=':
7962 matches = fieldStr.indexOf(valStr) === 0;
7963 break;
7964
7965 case '=':
7966 matches = fieldVal === value;
7967 break;
7968
7969 case '>':
7970 isIneqCmp = true;
7971 matches = fieldVal > value;
7972 break;
7973
7974 case '>=':
7975 isIneqCmp = true;
7976 matches = fieldVal >= value;
7977 break;
7978
7979 case '<':
7980 isIneqCmp = true;
7981 matches = fieldVal < value;
7982 break;
7983
7984 case '<=':
7985 isIneqCmp = true;
7986 matches = fieldVal <= value;
7987 break;
7988
7989 default:
7990 matches = false;
7991 break;
7992 } // apply the not op, but null vals for inequalities should always stay non-matching
7993
7994
7995 if (notExpr && (fieldVal != null || !isIneqCmp)) {
7996 matches = !matches;
7997 }
7998
7999 return matches;
8000};
8001var boolCmp = function boolCmp(fieldVal, operator) {
8002 switch (operator) {
8003 case '?':
8004 return fieldVal ? true : false;
8005
8006 case '!':
8007 return fieldVal ? false : true;
8008
8009 case '^':
8010 return fieldVal === undefined;
8011 }
8012};
8013var existCmp = function existCmp(fieldVal) {
8014 return fieldVal !== undefined;
8015};
8016var data = function data(ele, field) {
8017 return ele.data(field);
8018};
8019var meta = function meta(ele, field) {
8020 return ele[field]();
8021};
8022
8023/** A lookup of `match(check, ele)` functions by `Type` int */
8024
8025var match = [];
8026/**
8027 * Returns whether the query matches for the element
8028 * @param query The `{ type, value, ... }` query object
8029 * @param ele The element to compare against
8030*/
8031
8032var matches = function matches(query, ele) {
8033 return query.checks.every(function (chk) {
8034 return match[chk.type](chk, ele);
8035 });
8036};
8037
8038match[Type.GROUP] = function (check, ele) {
8039 var group = check.value;
8040 return group === '*' || group === ele.group();
8041};
8042
8043match[Type.STATE] = function (check, ele) {
8044 var stateSelector = check.value;
8045 return stateSelectorMatches(stateSelector, ele);
8046};
8047
8048match[Type.ID] = function (check, ele) {
8049 var id = check.value;
8050 return ele.id() === id;
8051};
8052
8053match[Type.CLASS] = function (check, ele) {
8054 var cls = check.value;
8055 return ele.hasClass(cls);
8056};
8057
8058match[Type.META_COMPARE] = function (check, ele) {
8059 var field = check.field,
8060 operator = check.operator,
8061 value = check.value;
8062 return valCmp(meta(ele, field), operator, value);
8063};
8064
8065match[Type.DATA_COMPARE] = function (check, ele) {
8066 var field = check.field,
8067 operator = check.operator,
8068 value = check.value;
8069 return valCmp(data(ele, field), operator, value);
8070};
8071
8072match[Type.DATA_BOOL] = function (check, ele) {
8073 var field = check.field,
8074 operator = check.operator;
8075 return boolCmp(data(ele, field), operator);
8076};
8077
8078match[Type.DATA_EXIST] = function (check, ele) {
8079 var field = check.field,
8080 operator = check.operator;
8081 return existCmp(data(ele, field));
8082};
8083
8084match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8085 var qA = check.nodes[0];
8086 var qB = check.nodes[1];
8087 var src = ele.source();
8088 var tgt = ele.target();
8089 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
8090};
8091
8092match[Type.NODE_NEIGHBOR] = function (check, ele) {
8093 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
8094 return n.isNode() && matches(check.neighbor, n);
8095 });
8096};
8097
8098match[Type.DIRECTED_EDGE] = function (check, ele) {
8099 return matches(check.source, ele.source()) && matches(check.target, ele.target());
8100};
8101
8102match[Type.NODE_SOURCE] = function (check, ele) {
8103 return matches(check.source, ele) && ele.outgoers().some(function (n) {
8104 return n.isNode() && matches(check.target, n);
8105 });
8106};
8107
8108match[Type.NODE_TARGET] = function (check, ele) {
8109 return matches(check.target, ele) && ele.incomers().some(function (n) {
8110 return n.isNode() && matches(check.source, n);
8111 });
8112};
8113
8114match[Type.CHILD] = function (check, ele) {
8115 return matches(check.child, ele) && matches(check.parent, ele.parent());
8116};
8117
8118match[Type.PARENT] = function (check, ele) {
8119 return matches(check.parent, ele) && ele.children().some(function (c) {
8120 return matches(check.child, c);
8121 });
8122};
8123
8124match[Type.DESCENDANT] = function (check, ele) {
8125 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
8126 return matches(check.ancestor, a);
8127 });
8128};
8129
8130match[Type.ANCESTOR] = function (check, ele) {
8131 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
8132 return matches(check.descendant, d);
8133 });
8134};
8135
8136match[Type.COMPOUND_SPLIT] = function (check, ele) {
8137 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
8138};
8139
8140match[Type.TRUE] = function () {
8141 return true;
8142};
8143
8144match[Type.COLLECTION] = function (check, ele) {
8145 var collection = check.value;
8146 return collection.has(ele);
8147};
8148
8149match[Type.FILTER] = function (check, ele) {
8150 var filter = check.value;
8151 return filter(ele);
8152};
8153
8154var filter = function filter(collection) {
8155 var self = this; // for 1 id #foo queries, just get the element
8156
8157 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8158 return collection.getElementById(self[0].checks[0].value).collection();
8159 }
8160
8161 var selectorFunction = function selectorFunction(element) {
8162 for (var j = 0; j < self.length; j++) {
8163 var query = self[j];
8164
8165 if (matches(query, element)) {
8166 return true;
8167 }
8168 }
8169
8170 return false;
8171 };
8172
8173 if (self.text() == null) {
8174 selectorFunction = function selectorFunction() {
8175 return true;
8176 };
8177 }
8178
8179 return collection.filter(selectorFunction);
8180}; // filter
8181// does selector match a single element?
8182
8183
8184var matches$1 = function matches$1(ele) {
8185 var self = this;
8186
8187 for (var j = 0; j < self.length; j++) {
8188 var query = self[j];
8189
8190 if (matches(query, ele)) {
8191 return true;
8192 }
8193 }
8194
8195 return false;
8196}; // matches
8197
8198
8199var matching = {
8200 matches: matches$1,
8201 filter: filter
8202};
8203
8204var Selector = function Selector(selector) {
8205 this.inputText = selector;
8206 this.currentSubject = null;
8207 this.compoundCount = 0;
8208 this.edgeCount = 0;
8209 this.length = 0;
8210
8211 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8212 this.addQuery({
8213 checks: [{
8214 type: Type.COLLECTION,
8215 value: selector.collection()
8216 }]
8217 });
8218 } else if (fn(selector)) {
8219 this.addQuery({
8220 checks: [{
8221 type: Type.FILTER,
8222 value: selector
8223 }]
8224 });
8225 } else if (string(selector)) {
8226 if (!this.parse(selector)) {
8227 this.invalid = true;
8228 }
8229 } else {
8230 error('A selector must be created from a string; found ');
8231 }
8232};
8233
8234var selfn = Selector.prototype;
8235[parse$1, matching].forEach(function (p) {
8236 return extend(selfn, p);
8237});
8238
8239selfn.text = function () {
8240 return this.inputText;
8241};
8242
8243selfn.size = function () {
8244 return this.length;
8245};
8246
8247selfn.eq = function (i) {
8248 return this[i];
8249};
8250
8251selfn.sameText = function (otherSel) {
8252 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8253};
8254
8255selfn.addQuery = function (q) {
8256 this[this.length++] = q;
8257};
8258
8259selfn.selector = selfn.toString;
8260
8261var elesfn$f = {
8262 allAre: function allAre(selector) {
8263 var selObj = new Selector(selector);
8264 return this.every(function (ele) {
8265 return selObj.matches(ele);
8266 });
8267 },
8268 is: function is(selector) {
8269 var selObj = new Selector(selector);
8270 return this.some(function (ele) {
8271 return selObj.matches(ele);
8272 });
8273 },
8274 some: function some(fn, thisArg) {
8275 for (var i = 0; i < this.length; i++) {
8276 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8277
8278 if (ret) {
8279 return true;
8280 }
8281 }
8282
8283 return false;
8284 },
8285 every: function every(fn, thisArg) {
8286 for (var i = 0; i < this.length; i++) {
8287 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8288
8289 if (!ret) {
8290 return false;
8291 }
8292 }
8293
8294 return true;
8295 },
8296 same: function same(collection) {
8297 // cheap collection ref check
8298 if (this === collection) {
8299 return true;
8300 }
8301
8302 collection = this.cy().collection(collection);
8303 var thisLength = this.length;
8304 var collectionLength = collection.length; // cheap length check
8305
8306 if (thisLength !== collectionLength) {
8307 return false;
8308 } // cheap element ref check
8309
8310
8311 if (thisLength === 1) {
8312 return this[0] === collection[0];
8313 }
8314
8315 return this.every(function (ele) {
8316 return collection.hasElementWithId(ele.id());
8317 });
8318 },
8319 anySame: function anySame(collection) {
8320 collection = this.cy().collection(collection);
8321 return this.some(function (ele) {
8322 return collection.hasElementWithId(ele.id());
8323 });
8324 },
8325 allAreNeighbors: function allAreNeighbors(collection) {
8326 collection = this.cy().collection(collection);
8327 var nhood = this.neighborhood();
8328 return collection.every(function (ele) {
8329 return nhood.hasElementWithId(ele.id());
8330 });
8331 },
8332 contains: function contains(collection) {
8333 collection = this.cy().collection(collection);
8334 var self = this;
8335 return collection.every(function (ele) {
8336 return self.hasElementWithId(ele.id());
8337 });
8338 }
8339};
8340elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
8341elesfn$f.has = elesfn$f.contains;
8342elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
8343
8344var cache = function cache(fn, name) {
8345 return function traversalCache(arg1, arg2, arg3, arg4) {
8346 var selectorOrEles = arg1;
8347 var eles = this;
8348 var key;
8349
8350 if (selectorOrEles == null) {
8351 key = '';
8352 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
8353 key = selectorOrEles.id();
8354 }
8355
8356 if (eles.length === 1 && key) {
8357 var _p = eles[0]._private;
8358 var tch = _p.traversalCache = _p.traversalCache || {};
8359 var ch = tch[name] = tch[name] || [];
8360 var hash = hashString(key);
8361 var cacheHit = ch[hash];
8362
8363 if (cacheHit) {
8364 return cacheHit;
8365 } else {
8366 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
8367 }
8368 } else {
8369 return fn.call(eles, arg1, arg2, arg3, arg4);
8370 }
8371 };
8372};
8373
8374var elesfn$g = {
8375 parent: function parent(selector) {
8376 var parents = []; // optimisation for single ele call
8377
8378 if (this.length === 1) {
8379 var parent = this[0]._private.parent;
8380
8381 if (parent) {
8382 return parent;
8383 }
8384 }
8385
8386 for (var i = 0; i < this.length; i++) {
8387 var ele = this[i];
8388 var _parent = ele._private.parent;
8389
8390 if (_parent) {
8391 parents.push(_parent);
8392 }
8393 }
8394
8395 return this.spawn(parents, true).filter(selector);
8396 },
8397 parents: function parents(selector) {
8398 var parents = [];
8399 var eles = this.parent();
8400
8401 while (eles.nonempty()) {
8402 for (var i = 0; i < eles.length; i++) {
8403 var ele = eles[i];
8404 parents.push(ele);
8405 }
8406
8407 eles = eles.parent();
8408 }
8409
8410 return this.spawn(parents, true).filter(selector);
8411 },
8412 commonAncestors: function commonAncestors(selector) {
8413 var ancestors;
8414
8415 for (var i = 0; i < this.length; i++) {
8416 var ele = this[i];
8417 var parents = ele.parents();
8418 ancestors = ancestors || parents;
8419 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
8420 }
8421
8422 return ancestors.filter(selector);
8423 },
8424 orphans: function orphans(selector) {
8425 return this.stdFilter(function (ele) {
8426 return ele.isOrphan();
8427 }).filter(selector);
8428 },
8429 nonorphans: function nonorphans(selector) {
8430 return this.stdFilter(function (ele) {
8431 return ele.isChild();
8432 }).filter(selector);
8433 },
8434 children: cache(function (selector) {
8435 var children = [];
8436
8437 for (var i = 0; i < this.length; i++) {
8438 var ele = this[i];
8439 var eleChildren = ele._private.children;
8440
8441 for (var j = 0; j < eleChildren.length; j++) {
8442 children.push(eleChildren[j]);
8443 }
8444 }
8445
8446 return this.spawn(children, true).filter(selector);
8447 }, 'children'),
8448 siblings: function siblings(selector) {
8449 return this.parent().children().not(this).filter(selector);
8450 },
8451 isParent: function isParent() {
8452 var ele = this[0];
8453
8454 if (ele) {
8455 return ele.isNode() && ele._private.children.length !== 0;
8456 }
8457 },
8458 isChildless: function isChildless() {
8459 var ele = this[0];
8460
8461 if (ele) {
8462 return ele.isNode() && ele._private.children.length === 0;
8463 }
8464 },
8465 isChild: function isChild() {
8466 var ele = this[0];
8467
8468 if (ele) {
8469 return ele.isNode() && ele._private.parent != null;
8470 }
8471 },
8472 isOrphan: function isOrphan() {
8473 var ele = this[0];
8474
8475 if (ele) {
8476 return ele.isNode() && ele._private.parent == null;
8477 }
8478 },
8479 descendants: function descendants(selector) {
8480 var elements = [];
8481
8482 function add(eles) {
8483 for (var i = 0; i < eles.length; i++) {
8484 var ele = eles[i];
8485 elements.push(ele);
8486
8487 if (ele.children().nonempty()) {
8488 add(ele.children());
8489 }
8490 }
8491 }
8492
8493 add(this.children());
8494 return this.spawn(elements, true).filter(selector);
8495 }
8496};
8497
8498function forEachCompound(eles, fn, includeSelf, recursiveStep) {
8499 var q = [];
8500 var did = new Set$1();
8501 var cy = eles.cy();
8502 var hasCompounds = cy.hasCompoundNodes();
8503
8504 for (var i = 0; i < eles.length; i++) {
8505 var ele = eles[i];
8506
8507 if (includeSelf) {
8508 q.push(ele);
8509 } else if (hasCompounds) {
8510 recursiveStep(q, did, ele);
8511 }
8512 }
8513
8514 while (q.length > 0) {
8515 var _ele = q.shift();
8516
8517 fn(_ele);
8518 did.add(_ele.id());
8519
8520 if (hasCompounds) {
8521 recursiveStep(q, did, _ele);
8522 }
8523 }
8524
8525 return eles;
8526}
8527
8528function addChildren(q, did, ele) {
8529 if (ele.isParent()) {
8530 var children = ele._private.children;
8531
8532 for (var i = 0; i < children.length; i++) {
8533 var child = children[i];
8534
8535 if (!did.has(child.id())) {
8536 q.push(child);
8537 }
8538 }
8539 }
8540} // very efficient version of eles.add( eles.descendants() ).forEach()
8541// for internal use
8542
8543
8544elesfn$g.forEachDown = function (fn) {
8545 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8546 return forEachCompound(this, fn, includeSelf, addChildren);
8547};
8548
8549function addParent(q, did, ele) {
8550 if (ele.isChild()) {
8551 var parent = ele._private.parent;
8552
8553 if (!did.has(parent.id())) {
8554 q.push(parent);
8555 }
8556 }
8557}
8558
8559elesfn$g.forEachUp = function (fn) {
8560 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8561 return forEachCompound(this, fn, includeSelf, addParent);
8562};
8563
8564function addParentAndChildren(q, did, ele) {
8565 addParent(q, did, ele);
8566 addChildren(q, did, ele);
8567}
8568
8569elesfn$g.forEachUpAndDown = function (fn) {
8570 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8571 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
8572}; // aliases
8573
8574
8575elesfn$g.ancestors = elesfn$g.parents;
8576
8577var fn$1, elesfn$h;
8578fn$1 = elesfn$h = {
8579 data: define$3.data({
8580 field: 'data',
8581 bindingEvent: 'data',
8582 allowBinding: true,
8583 allowSetting: true,
8584 settingEvent: 'data',
8585 settingTriggersEvent: true,
8586 triggerFnName: 'trigger',
8587 allowGetting: true,
8588 immutableKeys: {
8589 'id': true,
8590 'source': true,
8591 'target': true,
8592 'parent': true
8593 },
8594 updateStyle: true
8595 }),
8596 removeData: define$3.removeData({
8597 field: 'data',
8598 event: 'data',
8599 triggerFnName: 'trigger',
8600 triggerEvent: true,
8601 immutableKeys: {
8602 'id': true,
8603 'source': true,
8604 'target': true,
8605 'parent': true
8606 },
8607 updateStyle: true
8608 }),
8609 scratch: define$3.data({
8610 field: 'scratch',
8611 bindingEvent: 'scratch',
8612 allowBinding: true,
8613 allowSetting: true,
8614 settingEvent: 'scratch',
8615 settingTriggersEvent: true,
8616 triggerFnName: 'trigger',
8617 allowGetting: true,
8618 updateStyle: true
8619 }),
8620 removeScratch: define$3.removeData({
8621 field: 'scratch',
8622 event: 'scratch',
8623 triggerFnName: 'trigger',
8624 triggerEvent: true,
8625 updateStyle: true
8626 }),
8627 rscratch: define$3.data({
8628 field: 'rscratch',
8629 allowBinding: false,
8630 allowSetting: true,
8631 settingTriggersEvent: false,
8632 allowGetting: true
8633 }),
8634 removeRscratch: define$3.removeData({
8635 field: 'rscratch',
8636 triggerEvent: false
8637 }),
8638 id: function id() {
8639 var ele = this[0];
8640
8641 if (ele) {
8642 return ele._private.data.id;
8643 }
8644 }
8645}; // aliases
8646
8647fn$1.attr = fn$1.data;
8648fn$1.removeAttr = fn$1.removeData;
8649var data$1 = elesfn$h;
8650
8651var elesfn$i = {};
8652
8653function defineDegreeFunction(callback) {
8654 return function (includeLoops) {
8655 var self = this;
8656
8657 if (includeLoops === undefined) {
8658 includeLoops = true;
8659 }
8660
8661 if (self.length === 0) {
8662 return;
8663 }
8664
8665 if (self.isNode() && !self.removed()) {
8666 var degree = 0;
8667 var node = self[0];
8668 var connectedEdges = node._private.edges;
8669
8670 for (var i = 0; i < connectedEdges.length; i++) {
8671 var edge = connectedEdges[i];
8672
8673 if (!includeLoops && edge.isLoop()) {
8674 continue;
8675 }
8676
8677 degree += callback(node, edge);
8678 }
8679
8680 return degree;
8681 } else {
8682 return;
8683 }
8684 };
8685}
8686
8687extend(elesfn$i, {
8688 degree: defineDegreeFunction(function (node, edge) {
8689 if (edge.source().same(edge.target())) {
8690 return 2;
8691 } else {
8692 return 1;
8693 }
8694 }),
8695 indegree: defineDegreeFunction(function (node, edge) {
8696 if (edge.target().same(node)) {
8697 return 1;
8698 } else {
8699 return 0;
8700 }
8701 }),
8702 outdegree: defineDegreeFunction(function (node, edge) {
8703 if (edge.source().same(node)) {
8704 return 1;
8705 } else {
8706 return 0;
8707 }
8708 })
8709});
8710
8711function defineDegreeBoundsFunction(degreeFn, callback) {
8712 return function (includeLoops) {
8713 var ret;
8714 var nodes = this.nodes();
8715
8716 for (var i = 0; i < nodes.length; i++) {
8717 var ele = nodes[i];
8718 var degree = ele[degreeFn](includeLoops);
8719
8720 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
8721 ret = degree;
8722 }
8723 }
8724
8725 return ret;
8726 };
8727}
8728
8729extend(elesfn$i, {
8730 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
8731 return degree < min;
8732 }),
8733 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
8734 return degree > max;
8735 }),
8736 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
8737 return degree < min;
8738 }),
8739 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
8740 return degree > max;
8741 }),
8742 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
8743 return degree < min;
8744 }),
8745 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
8746 return degree > max;
8747 })
8748});
8749extend(elesfn$i, {
8750 totalDegree: function totalDegree(includeLoops) {
8751 var total = 0;
8752 var nodes = this.nodes();
8753
8754 for (var i = 0; i < nodes.length; i++) {
8755 total += nodes[i].degree(includeLoops);
8756 }
8757
8758 return total;
8759 }
8760});
8761
8762var fn$2, elesfn$j;
8763
8764var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
8765 for (var i = 0; i < eles.length; i++) {
8766 var ele = eles[i];
8767
8768 if (!ele.locked()) {
8769 var oldPos = ele._private.position;
8770 var delta = {
8771 x: newPos.x != null ? newPos.x - oldPos.x : 0,
8772 y: newPos.y != null ? newPos.y - oldPos.y : 0
8773 };
8774
8775 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
8776 ele.children().shift(delta, silent);
8777 }
8778
8779 ele.shiftCachedBoundingBox(delta);
8780 }
8781 }
8782};
8783
8784var positionDef = {
8785 field: 'position',
8786 bindingEvent: 'position',
8787 allowBinding: true,
8788 allowSetting: true,
8789 settingEvent: 'position',
8790 settingTriggersEvent: true,
8791 triggerFnName: 'emitAndNotify',
8792 allowGetting: true,
8793 validKeys: ['x', 'y'],
8794 beforeGet: function beforeGet(ele) {
8795 ele.updateCompoundBounds();
8796 },
8797 beforeSet: function beforeSet(eles, newPos) {
8798 beforePositionSet(eles, newPos, false);
8799 },
8800 onSet: function onSet(eles) {
8801 eles.dirtyCompoundBoundsCache();
8802 },
8803 canSet: function canSet(ele) {
8804 return !ele.locked();
8805 }
8806};
8807fn$2 = elesfn$j = {
8808 position: define$3.data(positionDef),
8809 // position but no notification to renderer
8810 silentPosition: define$3.data(extend({}, positionDef, {
8811 allowBinding: false,
8812 allowSetting: true,
8813 settingTriggersEvent: false,
8814 allowGetting: false,
8815 beforeSet: function beforeSet(eles, newPos) {
8816 beforePositionSet(eles, newPos, true);
8817 }
8818 })),
8819 positions: function positions(pos, silent) {
8820 if (plainObject(pos)) {
8821 if (silent) {
8822 this.silentPosition(pos);
8823 } else {
8824 this.position(pos);
8825 }
8826 } else if (fn(pos)) {
8827 var _fn = pos;
8828 var cy = this.cy();
8829 cy.startBatch();
8830
8831 for (var i = 0; i < this.length; i++) {
8832 var ele = this[i];
8833
8834 var _pos = void 0;
8835
8836 if (_pos = _fn(ele, i)) {
8837 if (silent) {
8838 ele.silentPosition(_pos);
8839 } else {
8840 ele.position(_pos);
8841 }
8842 }
8843 }
8844
8845 cy.endBatch();
8846 }
8847
8848 return this; // chaining
8849 },
8850 silentPositions: function silentPositions(pos) {
8851 return this.positions(pos, true);
8852 },
8853 shift: function shift(dim, val, silent) {
8854 var delta;
8855
8856 if (plainObject(dim)) {
8857 delta = {
8858 x: number(dim.x) ? dim.x : 0,
8859 y: number(dim.y) ? dim.y : 0
8860 };
8861 silent = val;
8862 } else if (string(dim) && number(val)) {
8863 delta = {
8864 x: 0,
8865 y: 0
8866 };
8867 delta[dim] = val;
8868 }
8869
8870 if (delta != null) {
8871 var cy = this.cy();
8872 cy.startBatch();
8873
8874 for (var i = 0; i < this.length; i++) {
8875 var ele = this[i];
8876 var pos = ele.position();
8877 var newPos = {
8878 x: pos.x + delta.x,
8879 y: pos.y + delta.y
8880 };
8881
8882 if (silent) {
8883 ele.silentPosition(newPos);
8884 } else {
8885 ele.position(newPos);
8886 }
8887 }
8888
8889 cy.endBatch();
8890 }
8891
8892 return this;
8893 },
8894 silentShift: function silentShift(dim, val) {
8895 if (plainObject(dim)) {
8896 this.shift(dim, true);
8897 } else if (string(dim) && number(val)) {
8898 this.shift(dim, val, true);
8899 }
8900
8901 return this;
8902 },
8903 // get/set the rendered (i.e. on screen) positon of the element
8904 renderedPosition: function renderedPosition(dim, val) {
8905 var ele = this[0];
8906 var cy = this.cy();
8907 var zoom = cy.zoom();
8908 var pan = cy.pan();
8909 var rpos = plainObject(dim) ? dim : undefined;
8910 var setting = rpos !== undefined || val !== undefined && string(dim);
8911
8912 if (ele && ele.isNode()) {
8913 // must have an element and must be a node to return position
8914 if (setting) {
8915 for (var i = 0; i < this.length; i++) {
8916 var _ele = this[i];
8917
8918 if (val !== undefined) {
8919 // set one dimension
8920 _ele.position(dim, (val - pan[dim]) / zoom);
8921 } else if (rpos !== undefined) {
8922 // set whole position
8923 _ele.position(renderedToModelPosition(rpos, zoom, pan));
8924 }
8925 }
8926 } else {
8927 // getting
8928 var pos = ele.position();
8929 rpos = modelToRenderedPosition(pos, zoom, pan);
8930
8931 if (dim === undefined) {
8932 // then return the whole rendered position
8933 return rpos;
8934 } else {
8935 // then return the specified dimension
8936 return rpos[dim];
8937 }
8938 }
8939 } else if (!setting) {
8940 return undefined; // for empty collection case
8941 }
8942
8943 return this; // chaining
8944 },
8945 // get/set the position relative to the parent
8946 relativePosition: function relativePosition(dim, val) {
8947 var ele = this[0];
8948 var cy = this.cy();
8949 var ppos = plainObject(dim) ? dim : undefined;
8950 var setting = ppos !== undefined || val !== undefined && string(dim);
8951 var hasCompoundNodes = cy.hasCompoundNodes();
8952
8953 if (ele && ele.isNode()) {
8954 // must have an element and must be a node to return position
8955 if (setting) {
8956 for (var i = 0; i < this.length; i++) {
8957 var _ele2 = this[i];
8958 var parent = hasCompoundNodes ? _ele2.parent() : null;
8959 var hasParent = parent && parent.length > 0;
8960 var relativeToParent = hasParent;
8961
8962 if (hasParent) {
8963 parent = parent[0];
8964 }
8965
8966 var origin = relativeToParent ? parent.position() : {
8967 x: 0,
8968 y: 0
8969 };
8970
8971 if (val !== undefined) {
8972 // set one dimension
8973 _ele2.position(dim, val + origin[dim]);
8974 } else if (ppos !== undefined) {
8975 // set whole position
8976 _ele2.position({
8977 x: ppos.x + origin.x,
8978 y: ppos.y + origin.y
8979 });
8980 }
8981 }
8982 } else {
8983 // getting
8984 var pos = ele.position();
8985
8986 var _parent = hasCompoundNodes ? ele.parent() : null;
8987
8988 var _hasParent = _parent && _parent.length > 0;
8989
8990 var _relativeToParent = _hasParent;
8991
8992 if (_hasParent) {
8993 _parent = _parent[0];
8994 }
8995
8996 var _origin = _relativeToParent ? _parent.position() : {
8997 x: 0,
8998 y: 0
8999 };
9000
9001 ppos = {
9002 x: pos.x - _origin.x,
9003 y: pos.y - _origin.y
9004 };
9005
9006 if (dim === undefined) {
9007 // then return the whole rendered position
9008 return ppos;
9009 } else {
9010 // then return the specified dimension
9011 return ppos[dim];
9012 }
9013 }
9014 } else if (!setting) {
9015 return undefined; // for empty collection case
9016 }
9017
9018 return this; // chaining
9019 }
9020}; // aliases
9021
9022fn$2.modelPosition = fn$2.point = fn$2.position;
9023fn$2.modelPositions = fn$2.points = fn$2.positions;
9024fn$2.renderedPoint = fn$2.renderedPosition;
9025fn$2.relativePoint = fn$2.relativePosition;
9026var position = elesfn$j;
9027
9028var fn$3, elesfn$k;
9029fn$3 = elesfn$k = {};
9030
9031elesfn$k.renderedBoundingBox = function (options) {
9032 var bb = this.boundingBox(options);
9033 var cy = this.cy();
9034 var zoom = cy.zoom();
9035 var pan = cy.pan();
9036 var x1 = bb.x1 * zoom + pan.x;
9037 var x2 = bb.x2 * zoom + pan.x;
9038 var y1 = bb.y1 * zoom + pan.y;
9039 var y2 = bb.y2 * zoom + pan.y;
9040 return {
9041 x1: x1,
9042 x2: x2,
9043 y1: y1,
9044 y2: y2,
9045 w: x2 - x1,
9046 h: y2 - y1
9047 };
9048};
9049
9050elesfn$k.dirtyCompoundBoundsCache = function () {
9051 var cy = this.cy();
9052
9053 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9054 return this;
9055 }
9056
9057 this.forEachUp(function (ele) {
9058 if (ele.isParent()) {
9059 var _p = ele._private;
9060 _p.compoundBoundsClean = false;
9061 _p.bbCache = null;
9062 ele.emitAndNotify('bounds');
9063 }
9064 });
9065 return this;
9066};
9067
9068elesfn$k.updateCompoundBounds = function () {
9069 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9070 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9071
9072 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9073 return this;
9074 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9075
9076
9077 if (!force && cy.batching()) {
9078 return this;
9079 }
9080
9081 function update(parent) {
9082 if (!parent.isParent()) {
9083 return;
9084 }
9085
9086 var _p = parent._private;
9087 var children = parent.children();
9088 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9089 var min = {
9090 width: {
9091 val: parent.pstyle('min-width').pfValue,
9092 left: parent.pstyle('min-width-bias-left'),
9093 right: parent.pstyle('min-width-bias-right')
9094 },
9095 height: {
9096 val: parent.pstyle('min-height').pfValue,
9097 top: parent.pstyle('min-height-bias-top'),
9098 bottom: parent.pstyle('min-height-bias-bottom')
9099 }
9100 };
9101 var bb = children.boundingBox({
9102 includeLabels: includeLabels,
9103 includeOverlays: false,
9104 // updating the compound bounds happens outside of the regular
9105 // cache cycle (i.e. before fired events)
9106 useCache: false
9107 });
9108 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9109
9110 if (bb.w === 0 || bb.h === 0) {
9111 bb = {
9112 w: parent.pstyle('width').pfValue,
9113 h: parent.pstyle('height').pfValue
9114 };
9115 bb.x1 = pos.x - bb.w / 2;
9116 bb.x2 = pos.x + bb.w / 2;
9117 bb.y1 = pos.y - bb.h / 2;
9118 bb.y2 = pos.y + bb.h / 2;
9119 }
9120
9121 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9122 var biasDiff = 0;
9123 var biasComplementDiff = 0;
9124 var biasTotal = propBias + propBiasComplement;
9125
9126 if (propDiff > 0 && biasTotal > 0) {
9127 biasDiff = propBias / biasTotal * propDiff;
9128 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9129 }
9130
9131 return {
9132 biasDiff: biasDiff,
9133 biasComplementDiff: biasComplementDiff
9134 };
9135 }
9136
9137 function computePaddingValues(width, height, paddingObject, relativeTo) {
9138 // Assuming percentage is number from 0 to 1
9139 if (paddingObject.units === '%') {
9140 switch (relativeTo) {
9141 case 'width':
9142 return width > 0 ? paddingObject.pfValue * width : 0;
9143
9144 case 'height':
9145 return height > 0 ? paddingObject.pfValue * height : 0;
9146
9147 case 'average':
9148 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9149
9150 case 'min':
9151 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9152
9153 case 'max':
9154 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9155
9156 default:
9157 return 0;
9158 }
9159 } else if (paddingObject.units === 'px') {
9160 return paddingObject.pfValue;
9161 } else {
9162 return 0;
9163 }
9164 }
9165
9166 var leftVal = min.width.left.value;
9167
9168 if (min.width.left.units === 'px' && min.width.val > 0) {
9169 leftVal = leftVal * 100 / min.width.val;
9170 }
9171
9172 var rightVal = min.width.right.value;
9173
9174 if (min.width.right.units === 'px' && min.width.val > 0) {
9175 rightVal = rightVal * 100 / min.width.val;
9176 }
9177
9178 var topVal = min.height.top.value;
9179
9180 if (min.height.top.units === 'px' && min.height.val > 0) {
9181 topVal = topVal * 100 / min.height.val;
9182 }
9183
9184 var bottomVal = min.height.bottom.value;
9185
9186 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9187 bottomVal = bottomVal * 100 / min.height.val;
9188 }
9189
9190 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9191 var diffLeft = widthBiasDiffs.biasDiff;
9192 var diffRight = widthBiasDiffs.biasComplementDiff;
9193 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9194 var diffTop = heightBiasDiffs.biasDiff;
9195 var diffBottom = heightBiasDiffs.biasComplementDiff;
9196 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9197 _p.autoWidth = Math.max(bb.w, min.width.val);
9198 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9199 _p.autoHeight = Math.max(bb.h, min.height.val);
9200 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9201 }
9202
9203 for (var i = 0; i < this.length; i++) {
9204 var ele = this[i];
9205 var _p = ele._private;
9206
9207 if (!_p.compoundBoundsClean) {
9208 update(ele);
9209
9210 if (!cy.batching()) {
9211 _p.compoundBoundsClean = true;
9212 }
9213 }
9214 }
9215
9216 return this;
9217};
9218
9219var noninf = function noninf(x) {
9220 if (x === Infinity || x === -Infinity) {
9221 return 0;
9222 }
9223
9224 return x;
9225};
9226
9227var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9228 // don't update with zero area boxes
9229 if (x2 - x1 === 0 || y2 - y1 === 0) {
9230 return;
9231 } // don't update with null dim
9232
9233
9234 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9235 return;
9236 }
9237
9238 b.x1 = x1 < b.x1 ? x1 : b.x1;
9239 b.x2 = x2 > b.x2 ? x2 : b.x2;
9240 b.y1 = y1 < b.y1 ? y1 : b.y1;
9241 b.y2 = y2 > b.y2 ? y2 : b.y2;
9242 b.w = b.x2 - b.x1;
9243 b.h = b.y2 - b.y1;
9244};
9245
9246var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
9247 if (b2 == null) {
9248 return b;
9249 }
9250
9251 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
9252};
9253
9254var prefixedProperty = function prefixedProperty(obj, field, prefix) {
9255 return getPrefixedProperty(obj, field, prefix);
9256};
9257
9258var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
9259 if (ele.cy().headless()) {
9260 return;
9261 }
9262
9263 var _p = ele._private;
9264 var rstyle = _p.rstyle;
9265 var halfArW = rstyle.arrowWidth / 2;
9266 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
9267 var x;
9268 var y;
9269
9270 if (arrowType !== 'none') {
9271 if (prefix === 'source') {
9272 x = rstyle.srcX;
9273 y = rstyle.srcY;
9274 } else if (prefix === 'target') {
9275 x = rstyle.tgtX;
9276 y = rstyle.tgtY;
9277 } else {
9278 x = rstyle.midX;
9279 y = rstyle.midY;
9280 } // always store the individual arrow bounds
9281
9282
9283 var bbs = _p.arrowBounds = _p.arrowBounds || {};
9284 var bb = bbs[prefix] = bbs[prefix] || {};
9285 bb.x1 = x - halfArW;
9286 bb.y1 = y - halfArW;
9287 bb.x2 = x + halfArW;
9288 bb.y2 = y + halfArW;
9289 bb.w = bb.x2 - bb.x1;
9290 bb.h = bb.y2 - bb.y1;
9291 expandBoundingBox(bb, 1);
9292 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
9293 }
9294};
9295
9296var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
9297 if (ele.cy().headless()) {
9298 return;
9299 }
9300
9301 var prefixDash;
9302
9303 if (prefix) {
9304 prefixDash = prefix + '-';
9305 } else {
9306 prefixDash = '';
9307 }
9308
9309 var _p = ele._private;
9310 var rstyle = _p.rstyle;
9311 var label = ele.pstyle(prefixDash + 'label').strValue;
9312
9313 if (label) {
9314 var halign = ele.pstyle('text-halign');
9315 var valign = ele.pstyle('text-valign');
9316 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
9317 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
9318 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
9319 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
9320 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
9321 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
9322 var isEdge = ele.isEdge();
9323 var rotation = ele.pstyle(prefixDash + 'text-rotation');
9324 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
9325 var borderWidth = ele.pstyle('text-border-width').pfValue;
9326 var halfBorderWidth = borderWidth / 2;
9327 var padding = ele.pstyle('text-background-padding').pfValue;
9328 var lh = labelHeight;
9329 var lw = labelWidth;
9330 var lw_2 = lw / 2;
9331 var lh_2 = lh / 2;
9332 var lx1, lx2, ly1, ly2;
9333
9334 if (isEdge) {
9335 lx1 = labelX - lw_2;
9336 lx2 = labelX + lw_2;
9337 ly1 = labelY - lh_2;
9338 ly2 = labelY + lh_2;
9339 } else {
9340 switch (halign.value) {
9341 case 'left':
9342 lx1 = labelX - lw;
9343 lx2 = labelX;
9344 break;
9345
9346 case 'center':
9347 lx1 = labelX - lw_2;
9348 lx2 = labelX + lw_2;
9349 break;
9350
9351 case 'right':
9352 lx1 = labelX;
9353 lx2 = labelX + lw;
9354 break;
9355 }
9356
9357 switch (valign.value) {
9358 case 'top':
9359 ly1 = labelY - lh;
9360 ly2 = labelY;
9361 break;
9362
9363 case 'center':
9364 ly1 = labelY - lh_2;
9365 ly2 = labelY + lh_2;
9366 break;
9367
9368 case 'bottom':
9369 ly1 = labelY;
9370 ly2 = labelY + lh;
9371 break;
9372 }
9373 } // shift by margin and expand by outline and border
9374
9375
9376 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding;
9377 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding;
9378 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding;
9379 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding; // always store the unrotated label bounds separately
9380
9381 var bbPrefix = prefix || 'main';
9382 var bbs = _p.labelBounds;
9383 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
9384 bb.x1 = lx1;
9385 bb.y1 = ly1;
9386 bb.x2 = lx2;
9387 bb.y2 = ly2;
9388 bb.w = lx2 - lx1;
9389 bb.h = ly2 - ly1;
9390 expandBoundingBox(bb, 1); // expand to work around browser dimension inaccuracies
9391
9392 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
9393 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
9394
9395 if (isAutorotate || isPfValue) {
9396 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
9397 var cos = Math.cos(theta);
9398 var sin = Math.sin(theta); // rotation point (default value for center-center)
9399
9400 var xo = (lx1 + lx2) / 2;
9401 var yo = (ly1 + ly2) / 2;
9402
9403 if (!isEdge) {
9404 switch (halign.value) {
9405 case 'left':
9406 xo = lx2;
9407 break;
9408
9409 case 'right':
9410 xo = lx1;
9411 break;
9412 }
9413
9414 switch (valign.value) {
9415 case 'top':
9416 yo = ly2;
9417 break;
9418
9419 case 'bottom':
9420 yo = ly1;
9421 break;
9422 }
9423 }
9424
9425 var rotate = function rotate(x, y) {
9426 x = x - xo;
9427 y = y - yo;
9428 return {
9429 x: x * cos - y * sin + xo,
9430 y: x * sin + y * cos + yo
9431 };
9432 };
9433
9434 var px1y1 = rotate(lx1, ly1);
9435 var px1y2 = rotate(lx1, ly2);
9436 var px2y1 = rotate(lx2, ly1);
9437 var px2y2 = rotate(lx2, ly2);
9438 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9439 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9440 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9441 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9442 }
9443
9444 var bbPrefixRot = bbPrefix + 'Rot';
9445 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
9446 bbRot.x1 = lx1;
9447 bbRot.y1 = ly1;
9448 bbRot.x2 = lx2;
9449 bbRot.y2 = ly2;
9450 bbRot.w = lx2 - lx1;
9451 bbRot.h = ly2 - ly1;
9452 updateBounds(bounds, lx1, ly1, lx2, ly2);
9453 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
9454 }
9455
9456 return bounds;
9457}; // get the bounding box of the elements (in raw model position)
9458
9459
9460var boundingBoxImpl = function boundingBoxImpl(ele, options) {
9461 var cy = ele._private.cy;
9462 var styleEnabled = cy.styleEnabled();
9463 var headless = cy.headless();
9464 var bounds = makeBoundingBox();
9465 var _p = ele._private;
9466 var isNode = ele.isNode();
9467 var isEdge = ele.isEdge();
9468 var ex1, ex2, ey1, ey2; // extrema of body / lines
9469
9470 var x, y; // node pos
9471
9472 var rstyle = _p.rstyle;
9473 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
9474 // (other factors like width values will be considered later in this function anyway)
9475
9476 var isDisplayed = function isDisplayed(ele) {
9477 return ele.pstyle('display').value !== 'none';
9478 };
9479
9480 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
9481 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
9482
9483 if (displayed) {
9484 // displayed suffices, since we will find zero area eles anyway
9485 var overlayOpacity = 0;
9486 var overlayPadding = 0;
9487
9488 if (styleEnabled && options.includeOverlays) {
9489 overlayOpacity = ele.pstyle('overlay-opacity').value;
9490
9491 if (overlayOpacity !== 0) {
9492 overlayPadding = ele.pstyle('overlay-padding').value;
9493 }
9494 }
9495
9496 var w = 0;
9497 var wHalf = 0;
9498
9499 if (styleEnabled) {
9500 w = ele.pstyle('width').pfValue;
9501 wHalf = w / 2;
9502 }
9503
9504 if (isNode && options.includeNodes) {
9505 var pos = ele.position();
9506 x = pos.x;
9507 y = pos.y;
9508
9509 var _w = ele.outerWidth();
9510
9511 var halfW = _w / 2;
9512 var h = ele.outerHeight();
9513 var halfH = h / 2; // handle node dimensions
9514 /////////////////////////
9515
9516 ex1 = x - halfW;
9517 ex2 = x + halfW;
9518 ey1 = y - halfH;
9519 ey2 = y + halfH;
9520 updateBounds(bounds, ex1, ey1, ex2, ey2);
9521 } else if (isEdge && options.includeEdges) {
9522 if (styleEnabled && !headless) {
9523 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
9524 //////////////////////////////////////////////
9525
9526 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9527 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9528 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
9529 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
9530
9531 ex1 -= wHalf;
9532 ex2 += wHalf;
9533 ey1 -= wHalf;
9534 ey2 += wHalf;
9535 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
9536 ////////////////
9537
9538 if (curveStyle === 'haystack') {
9539 var hpts = rstyle.haystackPts;
9540
9541 if (hpts && hpts.length === 2) {
9542 ex1 = hpts[0].x;
9543 ey1 = hpts[0].y;
9544 ex2 = hpts[1].x;
9545 ey2 = hpts[1].y;
9546
9547 if (ex1 > ex2) {
9548 var temp = ex1;
9549 ex1 = ex2;
9550 ex2 = temp;
9551 }
9552
9553 if (ey1 > ey2) {
9554 var _temp = ey1;
9555 ey1 = ey2;
9556 ey2 = _temp;
9557 }
9558
9559 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
9560 }
9561 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
9562 var pts;
9563
9564 switch (curveStyle) {
9565 case 'bezier':
9566 case 'unbundled-bezier':
9567 pts = rstyle.bezierPts;
9568 break;
9569
9570 case 'segments':
9571 case 'taxi':
9572 pts = rstyle.linePts;
9573 break;
9574 }
9575
9576 if (pts != null) {
9577 for (var j = 0; j < pts.length; j++) {
9578 var pt = pts[j];
9579 ex1 = pt.x - wHalf;
9580 ex2 = pt.x + wHalf;
9581 ey1 = pt.y - wHalf;
9582 ey2 = pt.y + wHalf;
9583 updateBounds(bounds, ex1, ey1, ex2, ey2);
9584 }
9585 }
9586 } // bezier-like or segment-like edge
9587
9588 } else {
9589 // headless or style disabled
9590 // fallback on source and target positions
9591 //////////////////////////////////////////
9592 var n1 = ele.source();
9593 var n1pos = n1.position();
9594 var n2 = ele.target();
9595 var n2pos = n2.position();
9596 ex1 = n1pos.x;
9597 ex2 = n2pos.x;
9598 ey1 = n1pos.y;
9599 ey2 = n2pos.y;
9600
9601 if (ex1 > ex2) {
9602 var _temp2 = ex1;
9603 ex1 = ex2;
9604 ex2 = _temp2;
9605 }
9606
9607 if (ey1 > ey2) {
9608 var _temp3 = ey1;
9609 ey1 = ey2;
9610 ey2 = _temp3;
9611 } // take into account edge width
9612
9613
9614 ex1 -= wHalf;
9615 ex2 += wHalf;
9616 ey1 -= wHalf;
9617 ey2 += wHalf;
9618 updateBounds(bounds, ex1, ey1, ex2, ey2);
9619 } // headless or style disabled
9620
9621 } // edges
9622 // handle edge arrow size
9623 /////////////////////////
9624
9625
9626 if (styleEnabled && options.includeEdges && isEdge) {
9627 updateBoundsFromArrow(bounds, ele, 'mid-source');
9628 updateBoundsFromArrow(bounds, ele, 'mid-target');
9629 updateBoundsFromArrow(bounds, ele, 'source');
9630 updateBoundsFromArrow(bounds, ele, 'target');
9631 } // ghost
9632 ////////
9633
9634
9635 if (styleEnabled) {
9636 var ghost = ele.pstyle('ghost').value === 'yes';
9637
9638 if (ghost) {
9639 var gx = ele.pstyle('ghost-offset-x').pfValue;
9640 var gy = ele.pstyle('ghost-offset-y').pfValue;
9641 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
9642 }
9643 } // always store the body bounds separately from the labels
9644
9645
9646 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
9647 assignBoundingBox(bbBody, bounds);
9648 expandBoundingBoxSides(bbBody, manualExpansion);
9649 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
9650 // overlay
9651 //////////
9652
9653 if (styleEnabled) {
9654 ex1 = bounds.x1;
9655 ex2 = bounds.x2;
9656 ey1 = bounds.y1;
9657 ey2 = bounds.y2;
9658 updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
9659 } // always store the body bounds separately from the labels
9660
9661
9662 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
9663 assignBoundingBox(bbOverlay, bounds);
9664 expandBoundingBoxSides(bbOverlay, manualExpansion);
9665 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
9666 // handle label dimensions
9667 //////////////////////////
9668
9669 var bbLabels = _p.labelBounds = _p.labelBounds || {};
9670
9671 if (bbLabels.all != null) {
9672 clearBoundingBox(bbLabels.all);
9673 } else {
9674 bbLabels.all = makeBoundingBox();
9675 }
9676
9677 if (styleEnabled && options.includeLabels) {
9678 if (options.includeMainLabels) {
9679 updateBoundsFromLabel(bounds, ele, null);
9680 }
9681
9682 if (isEdge) {
9683 if (options.includeSourceLabels) {
9684 updateBoundsFromLabel(bounds, ele, 'source');
9685 }
9686
9687 if (options.includeTargetLabels) {
9688 updateBoundsFromLabel(bounds, ele, 'target');
9689 }
9690 }
9691 } // style enabled for labels
9692
9693 } // if displayed
9694
9695
9696 bounds.x1 = noninf(bounds.x1);
9697 bounds.y1 = noninf(bounds.y1);
9698 bounds.x2 = noninf(bounds.x2);
9699 bounds.y2 = noninf(bounds.y2);
9700 bounds.w = noninf(bounds.x2 - bounds.x1);
9701 bounds.h = noninf(bounds.y2 - bounds.y1);
9702
9703 if (bounds.w > 0 && bounds.h > 0 && displayed) {
9704 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
9705
9706 expandBoundingBox(bounds, 1);
9707 }
9708
9709 return bounds;
9710};
9711
9712var getKey = function getKey(opts) {
9713 var i = 0;
9714
9715 var tf = function tf(val) {
9716 return (val ? 1 : 0) << i++;
9717 };
9718
9719 var key = 0;
9720 key += tf(opts.incudeNodes);
9721 key += tf(opts.includeEdges);
9722 key += tf(opts.includeLabels);
9723 key += tf(opts.includeMainLabels);
9724 key += tf(opts.includeSourceLabels);
9725 key += tf(opts.includeTargetLabels);
9726 key += tf(opts.includeOverlays);
9727 return key;
9728};
9729
9730var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
9731 if (ele.isEdge()) {
9732 var p1 = ele.source().position();
9733 var p2 = ele.target().position();
9734
9735 var r = function r(x) {
9736 return Math.round(x);
9737 };
9738
9739 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
9740 } else {
9741 return 0;
9742 }
9743};
9744
9745var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
9746 var _p = ele._private;
9747 var bb;
9748 var isEdge = ele.isEdge();
9749 var key = opts == null ? defBbOptsKey : getKey(opts);
9750 var usingDefOpts = key === defBbOptsKey;
9751 var currPosKey = getBoundingBoxPosKey(ele);
9752 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9753 var useCache = opts.useCache && isPosKeySame;
9754
9755 var isDirty = function isDirty(ele) {
9756 return ele._private.bbCache == null;
9757 };
9758
9759 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
9760
9761 if (needRecalc) {
9762 if (!isPosKeySame) {
9763 ele.recalculateRenderedStyle();
9764 }
9765
9766 bb = boundingBoxImpl(ele, defBbOpts);
9767 _p.bbCache = bb;
9768 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
9769 _p.bbCachePosKey = currPosKey;
9770 } else {
9771 bb = _p.bbCache;
9772 }
9773
9774 if (!needRecalc && (_p.bbCacheShift.x !== 0 || _p.bbCacheShift.y !== 0)) {
9775 var shift = assignShiftToBoundingBox;
9776 var delta = _p.bbCacheShift;
9777
9778 var safeShift = function safeShift(bb, delta) {
9779 if (bb != null) {
9780 shift(bb, delta);
9781 }
9782 };
9783
9784 shift(bb, delta);
9785 var bodyBounds = _p.bodyBounds,
9786 overlayBounds = _p.overlayBounds,
9787 labelBounds = _p.labelBounds,
9788 arrowBounds = _p.arrowBounds;
9789 safeShift(bodyBounds, delta);
9790 safeShift(overlayBounds, delta);
9791
9792 if (arrowBounds != null) {
9793 safeShift(arrowBounds.source, delta);
9794 safeShift(arrowBounds.target, delta);
9795 safeShift(arrowBounds['mid-source'], delta);
9796 safeShift(arrowBounds['mid-target'], delta);
9797 }
9798
9799 if (labelBounds != null) {
9800 safeShift(labelBounds.main, delta);
9801 safeShift(labelBounds.all, delta);
9802 safeShift(labelBounds.source, delta);
9803 safeShift(labelBounds.target, delta);
9804 }
9805 } // always reset the shift, because we either applied the shift or cleared it by doing a fresh recalc
9806
9807
9808 _p.bbCacheShift.x = _p.bbCacheShift.y = 0; // not using def opts => need to build up bb from combination of sub bbs
9809
9810 if (!usingDefOpts) {
9811 var isNode = ele.isNode();
9812 bb = makeBoundingBox();
9813
9814 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
9815 if (opts.includeOverlays) {
9816 updateBoundsFromBox(bb, _p.overlayBounds);
9817 } else {
9818 updateBoundsFromBox(bb, _p.bodyBounds);
9819 }
9820 }
9821
9822 if (opts.includeLabels) {
9823 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
9824 updateBoundsFromBox(bb, _p.labelBounds.all);
9825 } else {
9826 if (opts.includeMainLabels) {
9827 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
9828 }
9829
9830 if (opts.includeSourceLabels) {
9831 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
9832 }
9833
9834 if (opts.includeTargetLabels) {
9835 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
9836 }
9837 }
9838 }
9839
9840 bb.w = bb.x2 - bb.x1;
9841 bb.h = bb.y2 - bb.y1;
9842 }
9843
9844 return bb;
9845};
9846
9847var defBbOpts = {
9848 includeNodes: true,
9849 includeEdges: true,
9850 includeLabels: true,
9851 includeMainLabels: true,
9852 includeSourceLabels: true,
9853 includeTargetLabels: true,
9854 includeOverlays: true,
9855 useCache: true
9856};
9857var defBbOptsKey = getKey(defBbOpts);
9858var filledBbOpts = defaults(defBbOpts);
9859
9860elesfn$k.boundingBox = function (options) {
9861 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
9862 // specified s.t. the cache is used, so check for this case to make it faster by
9863 // avoiding the overhead of the rest of the function
9864
9865 if (this.length === 1 && this[0]._private.bbCache != null && (options === undefined || options.useCache === undefined || options.useCache === true)) {
9866 if (options === undefined) {
9867 options = defBbOpts;
9868 } else {
9869 options = filledBbOpts(options);
9870 }
9871
9872 bounds = cachedBoundingBoxImpl(this[0], options);
9873 } else {
9874 bounds = makeBoundingBox();
9875 options = options || defBbOpts;
9876 var opts = filledBbOpts(options);
9877 var eles = this;
9878 var cy = eles.cy();
9879 var styleEnabled = cy.styleEnabled();
9880
9881 if (styleEnabled) {
9882 for (var i = 0; i < eles.length; i++) {
9883 var ele = eles[i];
9884 var _p = ele._private;
9885 var currPosKey = getBoundingBoxPosKey(ele);
9886 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9887 var useCache = opts.useCache && isPosKeySame;
9888 ele.recalculateRenderedStyle(useCache);
9889 }
9890 }
9891
9892 this.updateCompoundBounds();
9893
9894 for (var _i = 0; _i < eles.length; _i++) {
9895 var _ele = eles[_i];
9896 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
9897 }
9898 }
9899
9900 bounds.x1 = noninf(bounds.x1);
9901 bounds.y1 = noninf(bounds.y1);
9902 bounds.x2 = noninf(bounds.x2);
9903 bounds.y2 = noninf(bounds.y2);
9904 bounds.w = noninf(bounds.x2 - bounds.x1);
9905 bounds.h = noninf(bounds.y2 - bounds.y1);
9906 return bounds;
9907};
9908
9909elesfn$k.dirtyBoundingBoxCache = function () {
9910 for (var i = 0; i < this.length; i++) {
9911 var _p = this[i]._private;
9912 _p.bbCache = null;
9913 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
9914 _p.bbCachePosKey = null;
9915 _p.bodyBounds = null;
9916 _p.overlayBounds = null;
9917 _p.labelBounds.all = null;
9918 _p.labelBounds.source = null;
9919 _p.labelBounds.target = null;
9920 _p.labelBounds.main = null;
9921 _p.labelBounds.sourceRot = null;
9922 _p.labelBounds.targetRot = null;
9923 _p.labelBounds.mainRot = null;
9924 _p.arrowBounds.source = null;
9925 _p.arrowBounds.target = null;
9926 _p.arrowBounds['mid-source'] = null;
9927 _p.arrowBounds['mid-target'] = null;
9928 }
9929
9930 this.emitAndNotify('bounds');
9931 return this;
9932};
9933
9934elesfn$k.shiftCachedBoundingBox = function (delta) {
9935 for (var i = 0; i < this.length; i++) {
9936 var ele = this[i];
9937 var _p = ele._private;
9938 var bb = _p.bbCache;
9939
9940 if (bb != null) {
9941 _p.bbCacheShift.x += delta.x;
9942 _p.bbCacheShift.y += delta.y;
9943 }
9944 }
9945
9946 this.emitAndNotify('bounds');
9947 return this;
9948}; // private helper to get bounding box for custom node positions
9949// - good for perf in certain cases but currently requires dirtying the rendered style
9950// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
9951// - try to use for only things like discrete layouts where the node position would change anyway
9952
9953
9954elesfn$k.boundingBoxAt = function (fn) {
9955 var nodes = this.nodes();
9956 var cy = this.cy();
9957 var hasCompoundNodes = cy.hasCompoundNodes();
9958
9959 if (hasCompoundNodes) {
9960 nodes = nodes.filter(function (node) {
9961 return !node.isParent();
9962 });
9963 }
9964
9965 if (plainObject(fn)) {
9966 var obj = fn;
9967
9968 fn = function fn() {
9969 return obj;
9970 };
9971 }
9972
9973 var storeOldPos = function storeOldPos(node, i) {
9974 return node._private.bbAtOldPos = fn(node, i);
9975 };
9976
9977 var getOldPos = function getOldPos(node) {
9978 return node._private.bbAtOldPos;
9979 };
9980
9981 cy.startBatch();
9982 nodes.forEach(storeOldPos).silentPositions(fn);
9983
9984 if (hasCompoundNodes) {
9985 this.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
9986 }
9987
9988 var bb = copyBoundingBox(this.boundingBox({
9989 useCache: false
9990 }));
9991 nodes.silentPositions(getOldPos);
9992 cy.endBatch();
9993 return bb;
9994};
9995
9996fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
9997fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
9998var bounds = elesfn$k;
9999
10000var fn$4, elesfn$l;
10001fn$4 = elesfn$l = {};
10002
10003var defineDimFns = function defineDimFns(opts) {
10004 opts.uppercaseName = capitalize(opts.name);
10005 opts.autoName = 'auto' + opts.uppercaseName;
10006 opts.labelName = 'label' + opts.uppercaseName;
10007 opts.outerName = 'outer' + opts.uppercaseName;
10008 opts.uppercaseOuterName = capitalize(opts.outerName);
10009
10010 fn$4[opts.name] = function dimImpl() {
10011 var ele = this[0];
10012 var _p = ele._private;
10013 var cy = _p.cy;
10014 var styleEnabled = cy._private.styleEnabled;
10015
10016 if (ele) {
10017 if (styleEnabled) {
10018 if (ele.isParent()) {
10019 ele.updateCompoundBounds();
10020 return _p[opts.autoName] || 0;
10021 }
10022
10023 var d = ele.pstyle(opts.name);
10024
10025 switch (d.strValue) {
10026 case 'label':
10027 ele.recalculateRenderedStyle();
10028 return _p.rstyle[opts.labelName] || 0;
10029
10030 default:
10031 return d.pfValue;
10032 }
10033 } else {
10034 return 1;
10035 }
10036 }
10037 };
10038
10039 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
10040 var ele = this[0];
10041 var _p = ele._private;
10042 var cy = _p.cy;
10043 var styleEnabled = cy._private.styleEnabled;
10044
10045 if (ele) {
10046 if (styleEnabled) {
10047 var dim = ele[opts.name]();
10048 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10049
10050 var padding = 2 * ele.padding();
10051 return dim + border + padding;
10052 } else {
10053 return 1;
10054 }
10055 }
10056 };
10057
10058 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10059 var ele = this[0];
10060
10061 if (ele) {
10062 var d = ele[opts.name]();
10063 return d * this.cy().zoom();
10064 }
10065 };
10066
10067 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10068 var ele = this[0];
10069
10070 if (ele) {
10071 var od = ele[opts.outerName]();
10072 return od * this.cy().zoom();
10073 }
10074 };
10075};
10076
10077defineDimFns({
10078 name: 'width'
10079});
10080defineDimFns({
10081 name: 'height'
10082});
10083
10084elesfn$l.padding = function () {
10085 var ele = this[0];
10086 var _p = ele._private;
10087
10088 if (ele.isParent()) {
10089 ele.updateCompoundBounds();
10090
10091 if (_p.autoPadding !== undefined) {
10092 return _p.autoPadding;
10093 } else {
10094 return ele.pstyle('padding').pfValue;
10095 }
10096 } else {
10097 return ele.pstyle('padding').pfValue;
10098 }
10099};
10100
10101elesfn$l.paddedHeight = function () {
10102 var ele = this[0];
10103 return ele.height() + 2 * ele.padding();
10104};
10105
10106elesfn$l.paddedWidth = function () {
10107 var ele = this[0];
10108 return ele.width() + 2 * ele.padding();
10109};
10110
10111var widthHeight = elesfn$l;
10112
10113var ifEdge = function ifEdge(ele, getValue) {
10114 if (ele.isEdge()) {
10115 return getValue(ele);
10116 }
10117};
10118
10119var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10120 if (ele.isEdge()) {
10121 var cy = ele.cy();
10122 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10123 }
10124};
10125
10126var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10127 if (ele.isEdge()) {
10128 var cy = ele.cy();
10129 var pan = cy.pan();
10130 var zoom = cy.zoom();
10131 return getPoints(ele).map(function (p) {
10132 return modelToRenderedPosition(p, zoom, pan);
10133 });
10134 }
10135};
10136
10137var controlPoints = function controlPoints(ele) {
10138 return ele.renderer().getControlPoints(ele);
10139};
10140
10141var segmentPoints = function segmentPoints(ele) {
10142 return ele.renderer().getSegmentPoints(ele);
10143};
10144
10145var sourceEndpoint = function sourceEndpoint(ele) {
10146 return ele.renderer().getSourceEndpoint(ele);
10147};
10148
10149var targetEndpoint = function targetEndpoint(ele) {
10150 return ele.renderer().getTargetEndpoint(ele);
10151};
10152
10153var midpoint = function midpoint(ele) {
10154 return ele.renderer().getEdgeMidpoint(ele);
10155};
10156
10157var pts = {
10158 controlPoints: {
10159 get: controlPoints,
10160 mult: true
10161 },
10162 segmentPoints: {
10163 get: segmentPoints,
10164 mult: true
10165 },
10166 sourceEndpoint: {
10167 get: sourceEndpoint
10168 },
10169 targetEndpoint: {
10170 get: targetEndpoint
10171 },
10172 midpoint: {
10173 get: midpoint
10174 }
10175};
10176
10177var renderedName = function renderedName(name) {
10178 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10179};
10180
10181var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10182 var spec = pts[name];
10183 var rName = renderedName(name);
10184
10185 obj[name] = function () {
10186 return ifEdge(this, spec.get);
10187 };
10188
10189 if (spec.mult) {
10190 obj[rName] = function () {
10191 return ifEdgeRenderedPositions(this, spec.get);
10192 };
10193 } else {
10194 obj[rName] = function () {
10195 return ifEdgeRenderedPosition(this, spec.get);
10196 };
10197 }
10198
10199 return obj;
10200}, {});
10201
10202var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10203
10204/*!
10205Event object based on jQuery events, MIT license
10206
10207https://jquery.org/license/
10208https://tldrlegal.com/license/mit-license
10209https://github.com/jquery/jquery/blob/master/src/event.js
10210*/
10211var Event = function Event(src, props) {
10212 this.recycle(src, props);
10213};
10214
10215function returnFalse() {
10216 return false;
10217}
10218
10219function returnTrue() {
10220 return true;
10221} // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10222
10223
10224Event.prototype = {
10225 instanceString: function instanceString() {
10226 return 'event';
10227 },
10228 recycle: function recycle(src, props) {
10229 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10230
10231 if (src != null && src.preventDefault) {
10232 // Browser Event object
10233 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10234 // by a handler lower down the tree; reflect the correct value.
10235
10236 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10237 } else if (src != null && src.type) {
10238 // Plain object containing all event details
10239 props = src;
10240 } else {
10241 // Event string
10242 this.type = src;
10243 } // Put explicitly provided properties onto the event object
10244
10245
10246 if (props != null) {
10247 // more efficient to manually copy fields we use
10248 this.originalEvent = props.originalEvent;
10249 this.type = props.type != null ? props.type : this.type;
10250 this.cy = props.cy;
10251 this.target = props.target;
10252 this.position = props.position;
10253 this.renderedPosition = props.renderedPosition;
10254 this.namespace = props.namespace;
10255 this.layout = props.layout;
10256 }
10257
10258 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10259 // create a rendered position based on the passed position
10260 var pos = this.position;
10261 var zoom = this.cy.zoom();
10262 var pan = this.cy.pan();
10263 this.renderedPosition = {
10264 x: pos.x * zoom + pan.x,
10265 y: pos.y * zoom + pan.y
10266 };
10267 } // Create a timestamp if incoming event doesn't have one
10268
10269
10270 this.timeStamp = src && src.timeStamp || Date.now();
10271 },
10272 preventDefault: function preventDefault() {
10273 this.isDefaultPrevented = returnTrue;
10274 var e = this.originalEvent;
10275
10276 if (!e) {
10277 return;
10278 } // if preventDefault exists run it on the original event
10279
10280
10281 if (e.preventDefault) {
10282 e.preventDefault();
10283 }
10284 },
10285 stopPropagation: function stopPropagation() {
10286 this.isPropagationStopped = returnTrue;
10287 var e = this.originalEvent;
10288
10289 if (!e) {
10290 return;
10291 } // if stopPropagation exists run it on the original event
10292
10293
10294 if (e.stopPropagation) {
10295 e.stopPropagation();
10296 }
10297 },
10298 stopImmediatePropagation: function stopImmediatePropagation() {
10299 this.isImmediatePropagationStopped = returnTrue;
10300 this.stopPropagation();
10301 },
10302 isDefaultPrevented: returnFalse,
10303 isPropagationStopped: returnFalse,
10304 isImmediatePropagationStopped: returnFalse
10305};
10306
10307var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
10308
10309var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
10310
10311var defaults$8 = {
10312 qualifierCompare: function qualifierCompare(q1, q2) {
10313 return q1 === q2;
10314 },
10315 eventMatches: function eventMatches()
10316 /*context, listener, eventObj*/
10317 {
10318 return true;
10319 },
10320 addEventFields: function addEventFields()
10321 /*context, evt*/
10322 {},
10323 callbackContext: function callbackContext(context
10324 /*, listener, eventObj*/
10325 ) {
10326 return context;
10327 },
10328 beforeEmit: function beforeEmit()
10329 /* context, listener, eventObj */
10330 {},
10331 afterEmit: function afterEmit()
10332 /* context, listener, eventObj */
10333 {},
10334 bubble: function bubble()
10335 /*context*/
10336 {
10337 return false;
10338 },
10339 parent: function parent()
10340 /*context*/
10341 {
10342 return null;
10343 },
10344 context: null
10345};
10346var defaultsKeys = Object.keys(defaults$8);
10347var emptyOpts = {};
10348
10349function Emitter() {
10350 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
10351 var context = arguments.length > 1 ? arguments[1] : undefined;
10352
10353 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
10354 for (var i = 0; i < defaultsKeys.length; i++) {
10355 var key = defaultsKeys[i];
10356 this[key] = opts[key] || defaults$8[key];
10357 }
10358
10359 this.context = context || this.context;
10360 this.listeners = [];
10361 this.emitting = 0;
10362}
10363
10364var p = Emitter.prototype;
10365
10366var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
10367 if (fn(qualifier)) {
10368 callback = qualifier;
10369 qualifier = null;
10370 }
10371
10372 if (confOverrides) {
10373 if (conf == null) {
10374 conf = confOverrides;
10375 } else {
10376 conf = extend({}, conf, confOverrides);
10377 }
10378 }
10379
10380 var eventList = array(events) ? events : events.split(/\s+/);
10381
10382 for (var i = 0; i < eventList.length; i++) {
10383 var evt = eventList[i];
10384
10385 if (emptyString(evt)) {
10386 continue;
10387 }
10388
10389 var match = evt.match(eventRegex); // type[.namespace]
10390
10391 if (match) {
10392 var type = match[1];
10393 var namespace = match[2] ? match[2] : null;
10394 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
10395
10396 if (ret === false) {
10397 break;
10398 } // allow exiting early
10399
10400 }
10401 }
10402};
10403
10404var makeEventObj = function makeEventObj(self, obj) {
10405 self.addEventFields(self.context, obj);
10406 return new Event(obj.type, obj);
10407};
10408
10409var forEachEventObj = function forEachEventObj(self, handler, events) {
10410 if (event(events)) {
10411 handler(self, events);
10412 return;
10413 } else if (plainObject(events)) {
10414 handler(self, makeEventObj(self, events));
10415 return;
10416 }
10417
10418 var eventList = array(events) ? events : events.split(/\s+/);
10419
10420 for (var i = 0; i < eventList.length; i++) {
10421 var evt = eventList[i];
10422
10423 if (emptyString(evt)) {
10424 continue;
10425 }
10426
10427 var match = evt.match(eventRegex); // type[.namespace]
10428
10429 if (match) {
10430 var type = match[1];
10431 var namespace = match[2] ? match[2] : null;
10432 var eventObj = makeEventObj(self, {
10433 type: type,
10434 namespace: namespace,
10435 target: self.context
10436 });
10437 handler(self, eventObj);
10438 }
10439 }
10440};
10441
10442p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
10443 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
10444 if (fn(callback)) {
10445 self.listeners.push({
10446 event: event,
10447 // full event string
10448 callback: callback,
10449 // callback to run
10450 type: type,
10451 // the event type (e.g. 'click')
10452 namespace: namespace,
10453 // the event namespace (e.g. ".foo")
10454 qualifier: qualifier,
10455 // a restriction on whether to match this emitter
10456 conf: conf // additional configuration
10457
10458 });
10459 }
10460 }, events, qualifier, callback, conf, confOverrides);
10461 return this;
10462};
10463
10464p.one = function (events, qualifier, callback, conf) {
10465 return this.on(events, qualifier, callback, conf, {
10466 one: true
10467 });
10468};
10469
10470p.removeListener = p.off = function (events, qualifier, callback, conf) {
10471 var _this = this;
10472
10473 if (this.emitting !== 0) {
10474 this.listeners = copyArray(this.listeners);
10475 }
10476
10477 var listeners = this.listeners;
10478
10479 var _loop = function _loop(i) {
10480 var listener = listeners[i];
10481 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
10482 /*, conf*/
10483 ) {
10484 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
10485 listeners.splice(i, 1);
10486 return false;
10487 }
10488 }, events, qualifier, callback, conf);
10489 };
10490
10491 for (var i = listeners.length - 1; i >= 0; i--) {
10492 _loop(i);
10493 }
10494
10495 return this;
10496};
10497
10498p.removeAllListeners = function () {
10499 return this.removeListener('*');
10500};
10501
10502p.emit = p.trigger = function (events, extraParams, manualCallback) {
10503 var listeners = this.listeners;
10504 var numListenersBeforeEmit = listeners.length;
10505 this.emitting++;
10506
10507 if (!array(extraParams)) {
10508 extraParams = [extraParams];
10509 }
10510
10511 forEachEventObj(this, function (self, eventObj) {
10512 if (manualCallback != null) {
10513 listeners = [{
10514 event: eventObj.event,
10515 type: eventObj.type,
10516 namespace: eventObj.namespace,
10517 callback: manualCallback
10518 }];
10519 numListenersBeforeEmit = listeners.length;
10520 }
10521
10522 var _loop2 = function _loop2(i) {
10523 var listener = listeners[i];
10524
10525 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
10526 var args = [eventObj];
10527
10528 if (extraParams != null) {
10529 push(args, extraParams);
10530 }
10531
10532 self.beforeEmit(self.context, listener, eventObj);
10533
10534 if (listener.conf && listener.conf.one) {
10535 self.listeners = self.listeners.filter(function (l) {
10536 return l !== listener;
10537 });
10538 }
10539
10540 var context = self.callbackContext(self.context, listener, eventObj);
10541 var ret = listener.callback.apply(context, args);
10542 self.afterEmit(self.context, listener, eventObj);
10543
10544 if (ret === false) {
10545 eventObj.stopPropagation();
10546 eventObj.preventDefault();
10547 }
10548 } // if listener matches
10549
10550 };
10551
10552 for (var i = 0; i < numListenersBeforeEmit; i++) {
10553 _loop2(i);
10554 } // for listener
10555
10556
10557 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
10558 self.parent(self.context).emit(eventObj, extraParams);
10559 }
10560 }, events);
10561 this.emitting--;
10562 return this;
10563};
10564
10565var emitterOptions = {
10566 qualifierCompare: function qualifierCompare(selector1, selector2) {
10567 if (selector1 == null || selector2 == null) {
10568 return selector1 == null && selector2 == null;
10569 } else {
10570 return selector1.sameText(selector2);
10571 }
10572 },
10573 eventMatches: function eventMatches(ele, listener, eventObj) {
10574 var selector = listener.qualifier;
10575
10576 if (selector != null) {
10577 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
10578 }
10579
10580 return true;
10581 },
10582 addEventFields: function addEventFields(ele, evt) {
10583 evt.cy = ele.cy();
10584 evt.target = ele;
10585 },
10586 callbackContext: function callbackContext(ele, listener, eventObj) {
10587 return listener.qualifier != null ? eventObj.target : ele;
10588 },
10589 beforeEmit: function beforeEmit(context, listener
10590 /*, eventObj*/
10591 ) {
10592 if (listener.conf && listener.conf.once) {
10593 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
10594 }
10595 },
10596 bubble: function bubble() {
10597 return true;
10598 },
10599 parent: function parent(ele) {
10600 return ele.isChild() ? ele.parent() : ele.cy();
10601 }
10602};
10603
10604var argSelector = function argSelector(arg) {
10605 if (string(arg)) {
10606 return new Selector(arg);
10607 } else {
10608 return arg;
10609 }
10610};
10611
10612var elesfn$m = {
10613 createEmitter: function createEmitter() {
10614 for (var i = 0; i < this.length; i++) {
10615 var ele = this[i];
10616 var _p = ele._private;
10617
10618 if (!_p.emitter) {
10619 _p.emitter = new Emitter(emitterOptions, ele);
10620 }
10621 }
10622
10623 return this;
10624 },
10625 emitter: function emitter() {
10626 return this._private.emitter;
10627 },
10628 on: function on(events, selector, callback) {
10629 var argSel = argSelector(selector);
10630
10631 for (var i = 0; i < this.length; i++) {
10632 var ele = this[i];
10633 ele.emitter().on(events, argSel, callback);
10634 }
10635
10636 return this;
10637 },
10638 removeListener: function removeListener(events, selector, callback) {
10639 var argSel = argSelector(selector);
10640
10641 for (var i = 0; i < this.length; i++) {
10642 var ele = this[i];
10643 ele.emitter().removeListener(events, argSel, callback);
10644 }
10645
10646 return this;
10647 },
10648 removeAllListeners: function removeAllListeners() {
10649 for (var i = 0; i < this.length; i++) {
10650 var ele = this[i];
10651 ele.emitter().removeAllListeners();
10652 }
10653
10654 return this;
10655 },
10656 one: function one(events, selector, callback) {
10657 var argSel = argSelector(selector);
10658
10659 for (var i = 0; i < this.length; i++) {
10660 var ele = this[i];
10661 ele.emitter().one(events, argSel, callback);
10662 }
10663
10664 return this;
10665 },
10666 once: function once(events, selector, callback) {
10667 var argSel = argSelector(selector);
10668
10669 for (var i = 0; i < this.length; i++) {
10670 var ele = this[i];
10671 ele.emitter().on(events, argSel, callback, {
10672 once: true,
10673 onceCollection: this
10674 });
10675 }
10676 },
10677 emit: function emit(events, extraParams) {
10678 for (var i = 0; i < this.length; i++) {
10679 var ele = this[i];
10680 ele.emitter().emit(events, extraParams);
10681 }
10682
10683 return this;
10684 },
10685 emitAndNotify: function emitAndNotify(event, extraParams) {
10686 // for internal use only
10687 if (this.length === 0) {
10688 return;
10689 } // empty collections don't need to notify anything
10690 // notify renderer
10691
10692
10693 this.cy().notify(event, this);
10694 this.emit(event, extraParams);
10695 return this;
10696 }
10697};
10698define$3.eventAliasesOn(elesfn$m);
10699
10700var elesfn$n = {
10701 nodes: function nodes(selector) {
10702 return this.filter(function (ele) {
10703 return ele.isNode();
10704 }).filter(selector);
10705 },
10706 edges: function edges(selector) {
10707 return this.filter(function (ele) {
10708 return ele.isEdge();
10709 }).filter(selector);
10710 },
10711 // internal helper to get nodes and edges as separate collections with single iteration over elements
10712 byGroup: function byGroup() {
10713 var nodes = this.spawn();
10714 var edges = this.spawn();
10715
10716 for (var i = 0; i < this.length; i++) {
10717 var ele = this[i];
10718
10719 if (ele.isNode()) {
10720 nodes.push(ele);
10721 } else {
10722 edges.push(ele);
10723 }
10724 }
10725
10726 return {
10727 nodes: nodes,
10728 edges: edges
10729 };
10730 },
10731 filter: function filter(_filter, thisArg) {
10732 if (_filter === undefined) {
10733 // check this first b/c it's the most common/performant case
10734 return this;
10735 } else if (string(_filter) || elementOrCollection(_filter)) {
10736 return new Selector(_filter).filter(this);
10737 } else if (fn(_filter)) {
10738 var filterEles = this.spawn();
10739 var eles = this;
10740
10741 for (var i = 0; i < eles.length; i++) {
10742 var ele = eles[i];
10743 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
10744
10745 if (include) {
10746 filterEles.push(ele);
10747 }
10748 }
10749
10750 return filterEles;
10751 }
10752
10753 return this.spawn(); // if not handled by above, give 'em an empty collection
10754 },
10755 not: function not(toRemove) {
10756 if (!toRemove) {
10757 return this;
10758 } else {
10759 if (string(toRemove)) {
10760 toRemove = this.filter(toRemove);
10761 }
10762
10763 var elements = this.spawn();
10764
10765 for (var i = 0; i < this.length; i++) {
10766 var element = this[i];
10767 var remove = toRemove.has(element);
10768
10769 if (!remove) {
10770 elements.push(element);
10771 }
10772 }
10773
10774 return elements;
10775 }
10776 },
10777 absoluteComplement: function absoluteComplement() {
10778 var cy = this.cy();
10779 return cy.mutableElements().not(this);
10780 },
10781 intersect: function intersect(other) {
10782 // if a selector is specified, then filter by it instead
10783 if (string(other)) {
10784 var selector = other;
10785 return this.filter(selector);
10786 }
10787
10788 var elements = this.spawn();
10789 var col1 = this;
10790 var col2 = other;
10791 var col1Smaller = this.length < other.length;
10792 var colS = col1Smaller ? col1 : col2;
10793 var colL = col1Smaller ? col2 : col1;
10794
10795 for (var i = 0; i < colS.length; i++) {
10796 var ele = colS[i];
10797
10798 if (colL.has(ele)) {
10799 elements.push(ele);
10800 }
10801 }
10802
10803 return elements;
10804 },
10805 xor: function xor(other) {
10806 var cy = this._private.cy;
10807
10808 if (string(other)) {
10809 other = cy.$(other);
10810 }
10811
10812 var elements = this.spawn();
10813 var col1 = this;
10814 var col2 = other;
10815
10816 var add = function add(col, other) {
10817 for (var i = 0; i < col.length; i++) {
10818 var ele = col[i];
10819 var id = ele._private.data.id;
10820 var inOther = other.hasElementWithId(id);
10821
10822 if (!inOther) {
10823 elements.push(ele);
10824 }
10825 }
10826 };
10827
10828 add(col1, col2);
10829 add(col2, col1);
10830 return elements;
10831 },
10832 diff: function diff(other) {
10833 var cy = this._private.cy;
10834
10835 if (string(other)) {
10836 other = cy.$(other);
10837 }
10838
10839 var left = this.spawn();
10840 var right = this.spawn();
10841 var both = this.spawn();
10842 var col1 = this;
10843 var col2 = other;
10844
10845 var add = function add(col, other, retEles) {
10846 for (var i = 0; i < col.length; i++) {
10847 var ele = col[i];
10848 var id = ele._private.data.id;
10849 var inOther = other.hasElementWithId(id);
10850
10851 if (inOther) {
10852 both.merge(ele);
10853 } else {
10854 retEles.push(ele);
10855 }
10856 }
10857 };
10858
10859 add(col1, col2, left);
10860 add(col2, col1, right);
10861 return {
10862 left: left,
10863 right: right,
10864 both: both
10865 };
10866 },
10867 add: function add(toAdd) {
10868 var cy = this._private.cy;
10869
10870 if (!toAdd) {
10871 return this;
10872 }
10873
10874 if (string(toAdd)) {
10875 var selector = toAdd;
10876 toAdd = cy.mutableElements().filter(selector);
10877 }
10878
10879 var elements = this.spawnSelf();
10880
10881 for (var i = 0; i < toAdd.length; i++) {
10882 var ele = toAdd[i];
10883 var add = !this.has(ele);
10884
10885 if (add) {
10886 elements.push(ele);
10887 }
10888 }
10889
10890 return elements;
10891 },
10892 // in place merge on calling collection
10893 merge: function merge(toAdd) {
10894 var _p = this._private;
10895 var cy = _p.cy;
10896
10897 if (!toAdd) {
10898 return this;
10899 }
10900
10901 if (toAdd && string(toAdd)) {
10902 var selector = toAdd;
10903 toAdd = cy.mutableElements().filter(selector);
10904 }
10905
10906 var map = _p.map;
10907
10908 for (var i = 0; i < toAdd.length; i++) {
10909 var toAddEle = toAdd[i];
10910 var id = toAddEle._private.data.id;
10911 var add = !map.has(id);
10912
10913 if (add) {
10914 var index = this.length++;
10915 this[index] = toAddEle;
10916 map.set(id, {
10917 ele: toAddEle,
10918 index: index
10919 });
10920 }
10921 }
10922
10923 return this; // chaining
10924 },
10925 unmergeAt: function unmergeAt(i) {
10926 var ele = this[i];
10927 var id = ele.id();
10928 var _p = this._private;
10929 var map = _p.map; // remove ele
10930
10931 this[i] = undefined;
10932 map["delete"](id);
10933 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
10934
10935 if (this.length > 1 && !unmergedLastEle) {
10936 var lastEleI = this.length - 1;
10937 var lastEle = this[lastEleI];
10938 var lastEleId = lastEle._private.data.id;
10939 this[lastEleI] = undefined;
10940 this[i] = lastEle;
10941 map.set(lastEleId, {
10942 ele: lastEle,
10943 index: i
10944 });
10945 } // the collection is now 1 ele smaller
10946
10947
10948 this.length--;
10949 return this;
10950 },
10951 // remove single ele in place in calling collection
10952 unmergeOne: function unmergeOne(ele) {
10953 ele = ele[0];
10954 var _p = this._private;
10955 var id = ele._private.data.id;
10956 var map = _p.map;
10957 var entry = map.get(id);
10958
10959 if (!entry) {
10960 return this; // no need to remove
10961 }
10962
10963 var i = entry.index;
10964 this.unmergeAt(i);
10965 return this;
10966 },
10967 // remove eles in place on calling collection
10968 unmerge: function unmerge(toRemove) {
10969 var cy = this._private.cy;
10970
10971 if (!toRemove) {
10972 return this;
10973 }
10974
10975 if (toRemove && string(toRemove)) {
10976 var selector = toRemove;
10977 toRemove = cy.mutableElements().filter(selector);
10978 }
10979
10980 for (var i = 0; i < toRemove.length; i++) {
10981 this.unmergeOne(toRemove[i]);
10982 }
10983
10984 return this; // chaining
10985 },
10986 unmergeBy: function unmergeBy(toRmFn) {
10987 for (var i = this.length - 1; i >= 0; i--) {
10988 var ele = this[i];
10989
10990 if (toRmFn(ele)) {
10991 this.unmergeAt(i);
10992 }
10993 }
10994
10995 return this;
10996 },
10997 map: function map(mapFn, thisArg) {
10998 var arr = [];
10999 var eles = this;
11000
11001 for (var i = 0; i < eles.length; i++) {
11002 var ele = eles[i];
11003 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11004 arr.push(ret);
11005 }
11006
11007 return arr;
11008 },
11009 reduce: function reduce(fn, initialValue) {
11010 var val = initialValue;
11011 var eles = this;
11012
11013 for (var i = 0; i < eles.length; i++) {
11014 val = fn(val, eles[i], i, eles);
11015 }
11016
11017 return val;
11018 },
11019 max: function max(valFn, thisArg) {
11020 var max = -Infinity;
11021 var maxEle;
11022 var eles = this;
11023
11024 for (var i = 0; i < eles.length; i++) {
11025 var ele = eles[i];
11026 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11027
11028 if (val > max) {
11029 max = val;
11030 maxEle = ele;
11031 }
11032 }
11033
11034 return {
11035 value: max,
11036 ele: maxEle
11037 };
11038 },
11039 min: function min(valFn, thisArg) {
11040 var min = Infinity;
11041 var minEle;
11042 var eles = this;
11043
11044 for (var i = 0; i < eles.length; i++) {
11045 var ele = eles[i];
11046 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11047
11048 if (val < min) {
11049 min = val;
11050 minEle = ele;
11051 }
11052 }
11053
11054 return {
11055 value: min,
11056 ele: minEle
11057 };
11058 }
11059}; // aliases
11060
11061var fn$5 = elesfn$n;
11062fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
11063fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
11064fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
11065fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
11066fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
11067fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
11068
11069var elesfn$o = {
11070 isNode: function isNode() {
11071 return this.group() === 'nodes';
11072 },
11073 isEdge: function isEdge() {
11074 return this.group() === 'edges';
11075 },
11076 isLoop: function isLoop() {
11077 return this.isEdge() && this.source()[0] === this.target()[0];
11078 },
11079 isSimple: function isSimple() {
11080 return this.isEdge() && this.source()[0] !== this.target()[0];
11081 },
11082 group: function group() {
11083 var ele = this[0];
11084
11085 if (ele) {
11086 return ele._private.group;
11087 }
11088 }
11089};
11090
11091/**
11092 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11093 * and z-index (low to high). These styles affect how this applies:
11094 *
11095 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11096 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11097 * root to leaves of the compound graph. The last drawn is `top`.
11098 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11099 * `manual` ignores this convention and draws based on the `z-index` value setting.
11100 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11101 * `z-index` will be drawn on top of an element with a lower `z-index`.
11102 */
11103
11104var zIndexSort = function zIndexSort(a, b) {
11105 var cy = a.cy();
11106 var hasCompoundNodes = cy.hasCompoundNodes();
11107
11108 function getDepth(ele) {
11109 var style = ele.pstyle('z-compound-depth');
11110
11111 if (style.value === 'auto') {
11112 return hasCompoundNodes ? ele.zDepth() : 0;
11113 } else if (style.value === 'bottom') {
11114 return -1;
11115 } else if (style.value === 'top') {
11116 return MAX_INT;
11117 } // 'orphan'
11118
11119
11120 return 0;
11121 }
11122
11123 var depthDiff = getDepth(a) - getDepth(b);
11124
11125 if (depthDiff !== 0) {
11126 return depthDiff;
11127 }
11128
11129 function getEleDepth(ele) {
11130 var style = ele.pstyle('z-index-compare');
11131
11132 if (style.value === 'auto') {
11133 return ele.isNode() ? 1 : 0;
11134 } // 'manual'
11135
11136
11137 return 0;
11138 }
11139
11140 var eleDiff = getEleDepth(a) - getEleDepth(b);
11141
11142 if (eleDiff !== 0) {
11143 return eleDiff;
11144 }
11145
11146 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11147
11148 if (zDiff !== 0) {
11149 return zDiff;
11150 } // compare indices in the core (order added to graph w/ last on top)
11151
11152
11153 return a.poolIndex() - b.poolIndex();
11154};
11155
11156var elesfn$p = {
11157 forEach: function forEach(fn$1, thisArg) {
11158 if (fn(fn$1)) {
11159 var N = this.length;
11160
11161 for (var i = 0; i < N; i++) {
11162 var ele = this[i];
11163 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
11164
11165 if (ret === false) {
11166 break;
11167 } // exit each early on return false
11168
11169 }
11170 }
11171
11172 return this;
11173 },
11174 toArray: function toArray() {
11175 var array = [];
11176
11177 for (var i = 0; i < this.length; i++) {
11178 array.push(this[i]);
11179 }
11180
11181 return array;
11182 },
11183 slice: function slice(start, end) {
11184 var array = [];
11185 var thisSize = this.length;
11186
11187 if (end == null) {
11188 end = thisSize;
11189 }
11190
11191 if (start == null) {
11192 start = 0;
11193 }
11194
11195 if (start < 0) {
11196 start = thisSize + start;
11197 }
11198
11199 if (end < 0) {
11200 end = thisSize + end;
11201 }
11202
11203 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11204 array.push(this[i]);
11205 }
11206
11207 return this.spawn(array);
11208 },
11209 size: function size() {
11210 return this.length;
11211 },
11212 eq: function eq(i) {
11213 return this[i] || this.spawn();
11214 },
11215 first: function first() {
11216 return this[0] || this.spawn();
11217 },
11218 last: function last() {
11219 return this[this.length - 1] || this.spawn();
11220 },
11221 empty: function empty() {
11222 return this.length === 0;
11223 },
11224 nonempty: function nonempty() {
11225 return !this.empty();
11226 },
11227 sort: function sort(sortFn) {
11228 if (!fn(sortFn)) {
11229 return this;
11230 }
11231
11232 var sorted = this.toArray().sort(sortFn);
11233 return this.spawn(sorted);
11234 },
11235 sortByZIndex: function sortByZIndex() {
11236 return this.sort(zIndexSort);
11237 },
11238 zDepth: function zDepth() {
11239 var ele = this[0];
11240
11241 if (!ele) {
11242 return undefined;
11243 } // let cy = ele.cy();
11244
11245
11246 var _p = ele._private;
11247 var group = _p.group;
11248
11249 if (group === 'nodes') {
11250 var depth = _p.data.parent ? ele.parents().size() : 0;
11251
11252 if (!ele.isParent()) {
11253 return MAX_INT - 1; // childless nodes always on top
11254 }
11255
11256 return depth;
11257 } else {
11258 var src = _p.source;
11259 var tgt = _p.target;
11260 var srcDepth = src.zDepth();
11261 var tgtDepth = tgt.zDepth();
11262 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11263 }
11264 }
11265};
11266elesfn$p.each = elesfn$p.forEach;
11267
11268var defineSymbolIterator = function defineSymbolIterator() {
11269 var typeofUndef = "undefined" ;
11270 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
11271
11272 if (isIteratorSupported) {
11273 elesfn$p[Symbol.iterator] = function () {
11274 var _this = this;
11275
11276 // eslint-disable-line no-undef
11277 var entry = {
11278 value: undefined,
11279 done: false
11280 };
11281 var i = 0;
11282 var length = this.length;
11283 return _defineProperty({
11284 next: function next() {
11285 if (i < length) {
11286 entry.value = _this[i++];
11287 } else {
11288 entry.value = undefined;
11289 entry.done = true;
11290 }
11291
11292 return entry;
11293 }
11294 }, Symbol.iterator, function () {
11295 // eslint-disable-line no-undef
11296 return this;
11297 });
11298 };
11299 }
11300};
11301
11302defineSymbolIterator();
11303
11304var getLayoutDimensionOptions = defaults({
11305 nodeDimensionsIncludeLabels: false
11306});
11307var elesfn$q = {
11308 // Calculates and returns node dimensions { x, y } based on options given
11309 layoutDimensions: function layoutDimensions(options) {
11310 options = getLayoutDimensionOptions(options);
11311 var dims;
11312
11313 if (!this.takesUpSpace()) {
11314 dims = {
11315 w: 0,
11316 h: 0
11317 };
11318 } else if (options.nodeDimensionsIncludeLabels) {
11319 var bbDim = this.boundingBox();
11320 dims = {
11321 w: bbDim.w,
11322 h: bbDim.h
11323 };
11324 } else {
11325 dims = {
11326 w: this.outerWidth(),
11327 h: this.outerHeight()
11328 };
11329 } // sanitise the dimensions for external layouts (avoid division by zero)
11330
11331
11332 if (dims.w === 0 || dims.h === 0) {
11333 dims.w = dims.h = 1;
11334 }
11335
11336 return dims;
11337 },
11338 // using standard layout options, apply position function (w/ or w/o animation)
11339 layoutPositions: function layoutPositions(layout, options, fn) {
11340 var nodes = this.nodes();
11341 var cy = this.cy();
11342 var layoutEles = options.eles; // nodes & edges
11343
11344 var getMemoizeKey = function getMemoizeKey(node) {
11345 return node.id();
11346 };
11347
11348 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
11349
11350 layout.emit({
11351 type: 'layoutstart',
11352 layout: layout
11353 });
11354 layout.animations = [];
11355
11356 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
11357 var center = {
11358 x: nodesBb.x1 + nodesBb.w / 2,
11359 y: nodesBb.y1 + nodesBb.h / 2
11360 };
11361 var spacingVector = {
11362 // scale from center of bounding box (not necessarily 0,0)
11363 x: (pos.x - center.x) * spacing,
11364 y: (pos.y - center.y) * spacing
11365 };
11366 return {
11367 x: center.x + spacingVector.x,
11368 y: center.y + spacingVector.y
11369 };
11370 };
11371
11372 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
11373
11374 var spacingBb = function spacingBb() {
11375 if (!useSpacingFactor) {
11376 return null;
11377 }
11378
11379 var bb = makeBoundingBox();
11380
11381 for (var i = 0; i < nodes.length; i++) {
11382 var node = nodes[i];
11383 var pos = fnMem(node, i);
11384 expandBoundingBoxByPoint(bb, pos.x, pos.y);
11385 }
11386
11387 return bb;
11388 };
11389
11390 var bb = spacingBb();
11391 var getFinalPos = memoize(function (node, i) {
11392 var newPos = fnMem(node, i);
11393
11394 if (useSpacingFactor) {
11395 var spacing = Math.abs(options.spacingFactor);
11396 newPos = calculateSpacing(spacing, bb, newPos);
11397 }
11398
11399 if (options.transform != null) {
11400 newPos = options.transform(node, newPos);
11401 }
11402
11403 return newPos;
11404 }, getMemoizeKey);
11405
11406 if (options.animate) {
11407 for (var i = 0; i < nodes.length; i++) {
11408 var node = nodes[i];
11409 var newPos = getFinalPos(node, i);
11410 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
11411
11412 if (animateNode) {
11413 var ani = node.animation({
11414 position: newPos,
11415 duration: options.animationDuration,
11416 easing: options.animationEasing
11417 });
11418 layout.animations.push(ani);
11419 } else {
11420 node.position(newPos);
11421 }
11422 }
11423
11424 if (options.fit) {
11425 var fitAni = cy.animation({
11426 fit: {
11427 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
11428 padding: options.padding
11429 },
11430 duration: options.animationDuration,
11431 easing: options.animationEasing
11432 });
11433 layout.animations.push(fitAni);
11434 } else if (options.zoom !== undefined && options.pan !== undefined) {
11435 var zoomPanAni = cy.animation({
11436 zoom: options.zoom,
11437 pan: options.pan,
11438 duration: options.animationDuration,
11439 easing: options.animationEasing
11440 });
11441 layout.animations.push(zoomPanAni);
11442 }
11443
11444 layout.animations.forEach(function (ani) {
11445 return ani.play();
11446 });
11447 layout.one('layoutready', options.ready);
11448 layout.emit({
11449 type: 'layoutready',
11450 layout: layout
11451 });
11452 Promise$1.all(layout.animations.map(function (ani) {
11453 return ani.promise();
11454 })).then(function () {
11455 layout.one('layoutstop', options.stop);
11456 layout.emit({
11457 type: 'layoutstop',
11458 layout: layout
11459 });
11460 });
11461 } else {
11462 nodes.positions(getFinalPos);
11463
11464 if (options.fit) {
11465 cy.fit(options.eles, options.padding);
11466 }
11467
11468 if (options.zoom != null) {
11469 cy.zoom(options.zoom);
11470 }
11471
11472 if (options.pan) {
11473 cy.pan(options.pan);
11474 }
11475
11476 layout.one('layoutready', options.ready);
11477 layout.emit({
11478 type: 'layoutready',
11479 layout: layout
11480 });
11481 layout.one('layoutstop', options.stop);
11482 layout.emit({
11483 type: 'layoutstop',
11484 layout: layout
11485 });
11486 }
11487
11488 return this; // chaining
11489 },
11490 layout: function layout(options) {
11491 var cy = this.cy();
11492 return cy.makeLayout(extend({}, options, {
11493 eles: this
11494 }));
11495 }
11496}; // aliases:
11497
11498elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
11499
11500function styleCache(key, fn, ele) {
11501 var _p = ele._private;
11502 var cache = _p.styleCache = _p.styleCache || [];
11503 var val;
11504
11505 if ((val = cache[key]) != null) {
11506 return val;
11507 } else {
11508 val = cache[key] = fn(ele);
11509 return val;
11510 }
11511}
11512
11513function cacheStyleFunction(key, fn) {
11514 key = hashString(key);
11515 return function cachedStyleFunction(ele) {
11516 return styleCache(key, fn, ele);
11517 };
11518}
11519
11520function cachePrototypeStyleFunction(key, fn) {
11521 key = hashString(key);
11522
11523 var selfFn = function selfFn(ele) {
11524 return fn.call(ele);
11525 };
11526
11527 return function cachedPrototypeStyleFunction() {
11528 var ele = this[0];
11529
11530 if (ele) {
11531 return styleCache(key, selfFn, ele);
11532 }
11533 };
11534}
11535
11536var elesfn$r = {
11537 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
11538 var cy = this.cy();
11539 var renderer = cy.renderer();
11540 var styleEnabled = cy.styleEnabled();
11541
11542 if (renderer && styleEnabled) {
11543 renderer.recalculateRenderedStyle(this, useCache);
11544 }
11545
11546 return this;
11547 },
11548 dirtyStyleCache: function dirtyStyleCache() {
11549 var cy = this.cy();
11550
11551 var dirty = function dirty(ele) {
11552 return ele._private.styleCache = null;
11553 };
11554
11555 if (cy.hasCompoundNodes()) {
11556 var eles;
11557 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11558 eles.merge(eles.connectedEdges());
11559 eles.forEach(dirty);
11560 } else {
11561 this.forEach(function (ele) {
11562 dirty(ele);
11563 ele.connectedEdges().forEach(dirty);
11564 });
11565 }
11566
11567 return this;
11568 },
11569 // fully updates (recalculates) the style for the elements
11570 updateStyle: function updateStyle(notifyRenderer) {
11571 var cy = this._private.cy;
11572
11573 if (!cy.styleEnabled()) {
11574 return this;
11575 }
11576
11577 if (cy.batching()) {
11578 var bEles = cy._private.batchStyleEles;
11579 bEles.merge(this);
11580 return this; // chaining and exit early when batching
11581 }
11582
11583 var hasCompounds = cy.hasCompoundNodes();
11584 var updatedEles = this;
11585 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
11586
11587 if (hasCompounds) {
11588 // then add everything up and down for compound selector checks
11589 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11590 } // let changedEles = style.apply( updatedEles );
11591
11592
11593 var changedEles = updatedEles;
11594
11595 if (notifyRenderer) {
11596 changedEles.emitAndNotify('style'); // let renderer know we changed style
11597 } else {
11598 changedEles.emit('style'); // just fire the event
11599 }
11600
11601 updatedEles.forEach(function (ele) {
11602 return ele._private.styleDirty = true;
11603 });
11604 return this; // chaining
11605 },
11606 // get the internal parsed style object for the specified property
11607 parsedStyle: function parsedStyle(property) {
11608 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11609 var ele = this[0];
11610 var cy = ele.cy();
11611
11612 if (!cy.styleEnabled()) {
11613 return;
11614 }
11615
11616 if (ele) {
11617 if (ele._private.styleDirty) {
11618 ele._private.styleDirty = false;
11619 cy.style().apply(ele);
11620 ele.emitAndNotify('style');
11621 }
11622
11623 var overriddenStyle = ele._private.style[property];
11624
11625 if (overriddenStyle != null) {
11626 return overriddenStyle;
11627 } else if (includeNonDefault) {
11628 return cy.style().getDefaultProperty(property);
11629 } else {
11630 return null;
11631 }
11632 }
11633 },
11634 numericStyle: function numericStyle(property) {
11635 var ele = this[0];
11636
11637 if (!ele.cy().styleEnabled()) {
11638 return;
11639 }
11640
11641 if (ele) {
11642 var pstyle = ele.pstyle(property);
11643 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
11644 }
11645 },
11646 numericStyleUnits: function numericStyleUnits(property) {
11647 var ele = this[0];
11648
11649 if (!ele.cy().styleEnabled()) {
11650 return;
11651 }
11652
11653 if (ele) {
11654 return ele.pstyle(property).units;
11655 }
11656 },
11657 // get the specified css property as a rendered value (i.e. on-screen value)
11658 // or get the whole rendered style if no property specified (NB doesn't allow setting)
11659 renderedStyle: function renderedStyle(property) {
11660 var cy = this.cy();
11661
11662 if (!cy.styleEnabled()) {
11663 return this;
11664 }
11665
11666 var ele = this[0];
11667
11668 if (ele) {
11669 return cy.style().getRenderedStyle(ele, property);
11670 }
11671 },
11672 // read the calculated css style of the element or override the style (via a bypass)
11673 style: function style(name, value) {
11674 var cy = this.cy();
11675
11676 if (!cy.styleEnabled()) {
11677 return this;
11678 }
11679
11680 var updateTransitions = false;
11681 var style = cy.style();
11682
11683 if (plainObject(name)) {
11684 // then extend the bypass
11685 var props = name;
11686 style.applyBypass(this, props, updateTransitions);
11687 this.emitAndNotify('style'); // let the renderer know we've updated style
11688 } else if (string(name)) {
11689 if (value === undefined) {
11690 // then get the property from the style
11691 var ele = this[0];
11692
11693 if (ele) {
11694 return style.getStylePropertyValue(ele, name);
11695 } else {
11696 // empty collection => can't get any value
11697 return;
11698 }
11699 } else {
11700 // then set the bypass with the property value
11701 style.applyBypass(this, name, value, updateTransitions);
11702 this.emitAndNotify('style'); // let the renderer know we've updated style
11703 }
11704 } else if (name === undefined) {
11705 var _ele = this[0];
11706
11707 if (_ele) {
11708 return style.getRawStyle(_ele);
11709 } else {
11710 // empty collection => can't get any value
11711 return;
11712 }
11713 }
11714
11715 return this; // chaining
11716 },
11717 removeStyle: function removeStyle(names) {
11718 var cy = this.cy();
11719
11720 if (!cy.styleEnabled()) {
11721 return this;
11722 }
11723
11724 var updateTransitions = false;
11725 var style = cy.style();
11726 var eles = this;
11727
11728 if (names === undefined) {
11729 for (var i = 0; i < eles.length; i++) {
11730 var ele = eles[i];
11731 style.removeAllBypasses(ele, updateTransitions);
11732 }
11733 } else {
11734 names = names.split(/\s+/);
11735
11736 for (var _i = 0; _i < eles.length; _i++) {
11737 var _ele2 = eles[_i];
11738 style.removeBypasses(_ele2, names, updateTransitions);
11739 }
11740 }
11741
11742 this.emitAndNotify('style'); // let the renderer know we've updated style
11743
11744 return this; // chaining
11745 },
11746 show: function show() {
11747 this.css('display', 'element');
11748 return this; // chaining
11749 },
11750 hide: function hide() {
11751 this.css('display', 'none');
11752 return this; // chaining
11753 },
11754 effectiveOpacity: function effectiveOpacity() {
11755 var cy = this.cy();
11756
11757 if (!cy.styleEnabled()) {
11758 return 1;
11759 }
11760
11761 var hasCompoundNodes = cy.hasCompoundNodes();
11762 var ele = this[0];
11763
11764 if (ele) {
11765 var _p = ele._private;
11766 var parentOpacity = ele.pstyle('opacity').value;
11767
11768 if (!hasCompoundNodes) {
11769 return parentOpacity;
11770 }
11771
11772 var parents = !_p.data.parent ? null : ele.parents();
11773
11774 if (parents) {
11775 for (var i = 0; i < parents.length; i++) {
11776 var parent = parents[i];
11777 var opacity = parent.pstyle('opacity').value;
11778 parentOpacity = opacity * parentOpacity;
11779 }
11780 }
11781
11782 return parentOpacity;
11783 }
11784 },
11785 transparent: function transparent() {
11786 var cy = this.cy();
11787
11788 if (!cy.styleEnabled()) {
11789 return false;
11790 }
11791
11792 var ele = this[0];
11793 var hasCompoundNodes = ele.cy().hasCompoundNodes();
11794
11795 if (ele) {
11796 if (!hasCompoundNodes) {
11797 return ele.pstyle('opacity').value === 0;
11798 } else {
11799 return ele.effectiveOpacity() === 0;
11800 }
11801 }
11802 },
11803 backgrounding: function backgrounding() {
11804 var cy = this.cy();
11805
11806 if (!cy.styleEnabled()) {
11807 return false;
11808 }
11809
11810 var ele = this[0];
11811 return ele._private.backgrounding ? true : false;
11812 }
11813};
11814
11815function checkCompound(ele, parentOk) {
11816 var _p = ele._private;
11817 var parents = _p.data.parent ? ele.parents() : null;
11818
11819 if (parents) {
11820 for (var i = 0; i < parents.length; i++) {
11821 var parent = parents[i];
11822
11823 if (!parentOk(parent)) {
11824 return false;
11825 }
11826 }
11827 }
11828
11829 return true;
11830}
11831
11832function defineDerivedStateFunction(specs) {
11833 var ok = specs.ok;
11834 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
11835 var parentOk = specs.parentOk || specs.ok;
11836 return function () {
11837 var cy = this.cy();
11838
11839 if (!cy.styleEnabled()) {
11840 return true;
11841 }
11842
11843 var ele = this[0];
11844 var hasCompoundNodes = cy.hasCompoundNodes();
11845
11846 if (ele) {
11847 var _p = ele._private;
11848
11849 if (!ok(ele)) {
11850 return false;
11851 }
11852
11853 if (ele.isNode()) {
11854 return !hasCompoundNodes || checkCompound(ele, parentOk);
11855 } else {
11856 var src = _p.source;
11857 var tgt = _p.target;
11858 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
11859 }
11860 }
11861 };
11862}
11863
11864var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
11865 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
11866});
11867elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
11868 ok: eleTakesUpSpace
11869}));
11870var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
11871 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
11872});
11873var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
11874 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
11875});
11876elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
11877 ok: eleInteractive,
11878 parentOk: parentInteractive,
11879 edgeOkViaNode: eleTakesUpSpace
11880}));
11881
11882elesfn$r.noninteractive = function () {
11883 var ele = this[0];
11884
11885 if (ele) {
11886 return !ele.interactive();
11887 }
11888};
11889
11890var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
11891 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
11892});
11893var edgeVisibleViaNode = eleTakesUpSpace;
11894elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
11895 ok: eleVisible,
11896 edgeOkViaNode: edgeVisibleViaNode
11897}));
11898
11899elesfn$r.hidden = function () {
11900 var ele = this[0];
11901
11902 if (ele) {
11903 return !ele.visible();
11904 }
11905};
11906
11907elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
11908 if (!this.cy().styleEnabled()) {
11909 return false;
11910 }
11911
11912 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
11913});
11914elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
11915elesfn$r.renderedCss = elesfn$r.renderedStyle;
11916elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
11917elesfn$r.pstyle = elesfn$r.parsedStyle;
11918
11919var elesfn$s = {};
11920
11921function defineSwitchFunction(params) {
11922 return function () {
11923 var args = arguments;
11924 var changedEles = []; // e.g. cy.nodes().select( data, handler )
11925
11926 if (args.length === 2) {
11927 var data = args[0];
11928 var handler = args[1];
11929 this.on(params.event, data, handler);
11930 } // e.g. cy.nodes().select( handler )
11931 else if (args.length === 1 && fn(args[0])) {
11932 var _handler = args[0];
11933 this.on(params.event, _handler);
11934 } // e.g. cy.nodes().select()
11935 // e.g. (private) cy.nodes().select(['tapselect'])
11936 else if (args.length === 0 || args.length === 1 && array(args[0])) {
11937 var addlEvents = args.length === 1 ? args[0] : null;
11938
11939 for (var i = 0; i < this.length; i++) {
11940 var ele = this[i];
11941 var able = !params.ableField || ele._private[params.ableField];
11942 var changed = ele._private[params.field] != params.value;
11943
11944 if (params.overrideAble) {
11945 var overrideAble = params.overrideAble(ele);
11946
11947 if (overrideAble !== undefined) {
11948 able = overrideAble;
11949
11950 if (!overrideAble) {
11951 return this;
11952 } // to save cycles assume not able for all on override
11953
11954 }
11955 }
11956
11957 if (able) {
11958 ele._private[params.field] = params.value;
11959
11960 if (changed) {
11961 changedEles.push(ele);
11962 }
11963 }
11964 }
11965
11966 var changedColl = this.spawn(changedEles);
11967 changedColl.updateStyle(); // change of state => possible change of style
11968
11969 changedColl.emit(params.event);
11970
11971 if (addlEvents) {
11972 changedColl.emit(addlEvents);
11973 }
11974 }
11975
11976 return this;
11977 };
11978}
11979
11980function defineSwitchSet(params) {
11981 elesfn$s[params.field] = function () {
11982 var ele = this[0];
11983
11984 if (ele) {
11985 if (params.overrideField) {
11986 var val = params.overrideField(ele);
11987
11988 if (val !== undefined) {
11989 return val;
11990 }
11991 }
11992
11993 return ele._private[params.field];
11994 }
11995 };
11996
11997 elesfn$s[params.on] = defineSwitchFunction({
11998 event: params.on,
11999 field: params.field,
12000 ableField: params.ableField,
12001 overrideAble: params.overrideAble,
12002 value: true
12003 });
12004 elesfn$s[params.off] = defineSwitchFunction({
12005 event: params.off,
12006 field: params.field,
12007 ableField: params.ableField,
12008 overrideAble: params.overrideAble,
12009 value: false
12010 });
12011}
12012
12013defineSwitchSet({
12014 field: 'locked',
12015 overrideField: function overrideField(ele) {
12016 return ele.cy().autolock() ? true : undefined;
12017 },
12018 on: 'lock',
12019 off: 'unlock'
12020});
12021defineSwitchSet({
12022 field: 'grabbable',
12023 overrideField: function overrideField(ele) {
12024 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12025 },
12026 on: 'grabify',
12027 off: 'ungrabify'
12028});
12029defineSwitchSet({
12030 field: 'selected',
12031 ableField: 'selectable',
12032 overrideAble: function overrideAble(ele) {
12033 return ele.cy().autounselectify() ? false : undefined;
12034 },
12035 on: 'select',
12036 off: 'unselect'
12037});
12038defineSwitchSet({
12039 field: 'selectable',
12040 overrideField: function overrideField(ele) {
12041 return ele.cy().autounselectify() ? false : undefined;
12042 },
12043 on: 'selectify',
12044 off: 'unselectify'
12045});
12046elesfn$s.deselect = elesfn$s.unselect;
12047
12048elesfn$s.grabbed = function () {
12049 var ele = this[0];
12050
12051 if (ele) {
12052 return ele._private.grabbed;
12053 }
12054};
12055
12056defineSwitchSet({
12057 field: 'active',
12058 on: 'activate',
12059 off: 'unactivate'
12060});
12061defineSwitchSet({
12062 field: 'pannable',
12063 on: 'panify',
12064 off: 'unpanify'
12065});
12066
12067elesfn$s.inactive = function () {
12068 var ele = this[0];
12069
12070 if (ele) {
12071 return !ele._private.active;
12072 }
12073};
12074
12075var elesfn$t = {}; // DAG functions
12076////////////////
12077
12078var defineDagExtremity = function defineDagExtremity(params) {
12079 return function dagExtremityImpl(selector) {
12080 var eles = this;
12081 var ret = [];
12082
12083 for (var i = 0; i < eles.length; i++) {
12084 var ele = eles[i];
12085
12086 if (!ele.isNode()) {
12087 continue;
12088 }
12089
12090 var disqualified = false;
12091 var edges = ele.connectedEdges();
12092
12093 for (var j = 0; j < edges.length; j++) {
12094 var edge = edges[j];
12095 var src = edge.source();
12096 var tgt = edge.target();
12097
12098 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12099 disqualified = true;
12100 break;
12101 }
12102 }
12103
12104 if (!disqualified) {
12105 ret.push(ele);
12106 }
12107 }
12108
12109 return this.spawn(ret, true).filter(selector);
12110 };
12111};
12112
12113var defineDagOneHop = function defineDagOneHop(params) {
12114 return function (selector) {
12115 var eles = this;
12116 var oEles = [];
12117
12118 for (var i = 0; i < eles.length; i++) {
12119 var ele = eles[i];
12120
12121 if (!ele.isNode()) {
12122 continue;
12123 }
12124
12125 var edges = ele.connectedEdges();
12126
12127 for (var j = 0; j < edges.length; j++) {
12128 var edge = edges[j];
12129 var src = edge.source();
12130 var tgt = edge.target();
12131
12132 if (params.outgoing && src === ele) {
12133 oEles.push(edge);
12134 oEles.push(tgt);
12135 } else if (params.incoming && tgt === ele) {
12136 oEles.push(edge);
12137 oEles.push(src);
12138 }
12139 }
12140 }
12141
12142 return this.spawn(oEles, true).filter(selector);
12143 };
12144};
12145
12146var defineDagAllHops = function defineDagAllHops(params) {
12147 return function (selector) {
12148 var eles = this;
12149 var sEles = [];
12150 var sElesIds = {};
12151
12152 for (;;) {
12153 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12154
12155 if (next.length === 0) {
12156 break;
12157 } // done if none left
12158
12159
12160 var newNext = false;
12161
12162 for (var i = 0; i < next.length; i++) {
12163 var n = next[i];
12164 var nid = n.id();
12165
12166 if (!sElesIds[nid]) {
12167 sElesIds[nid] = true;
12168 sEles.push(n);
12169 newNext = true;
12170 }
12171 }
12172
12173 if (!newNext) {
12174 break;
12175 } // done if touched all outgoers already
12176
12177
12178 eles = next;
12179 }
12180
12181 return this.spawn(sEles, true).filter(selector);
12182 };
12183};
12184
12185elesfn$t.clearTraversalCache = function () {
12186 for (var i = 0; i < this.length; i++) {
12187 this[i]._private.traversalCache = null;
12188 }
12189};
12190
12191extend(elesfn$t, {
12192 // get the root nodes in the DAG
12193 roots: defineDagExtremity({
12194 noIncomingEdges: true
12195 }),
12196 // get the leaf nodes in the DAG
12197 leaves: defineDagExtremity({
12198 noOutgoingEdges: true
12199 }),
12200 // normally called children in graph theory
12201 // these nodes =edges=> outgoing nodes
12202 outgoers: cache(defineDagOneHop({
12203 outgoing: true
12204 }), 'outgoers'),
12205 // aka DAG descendants
12206 successors: defineDagAllHops({
12207 outgoing: true
12208 }),
12209 // normally called parents in graph theory
12210 // these nodes <=edges= incoming nodes
12211 incomers: cache(defineDagOneHop({
12212 incoming: true
12213 }), 'incomers'),
12214 // aka DAG ancestors
12215 predecessors: defineDagAllHops({
12216 incoming: true
12217 })
12218}); // Neighbourhood functions
12219//////////////////////////
12220
12221extend(elesfn$t, {
12222 neighborhood: cache(function (selector) {
12223 var elements = [];
12224 var nodes = this.nodes();
12225
12226 for (var i = 0; i < nodes.length; i++) {
12227 // for all nodes
12228 var node = nodes[i];
12229 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12230
12231 for (var j = 0; j < connectedEdges.length; j++) {
12232 var edge = connectedEdges[j];
12233 var src = edge.source();
12234 var tgt = edge.target();
12235 var otherNode = node === src ? tgt : src; // need check in case of loop
12236
12237 if (otherNode.length > 0) {
12238 elements.push(otherNode[0]); // add node 1 hop away
12239 } // add connected edge
12240
12241
12242 elements.push(edge[0]);
12243 }
12244 }
12245
12246 return this.spawn(elements, true).filter(selector);
12247 }, 'neighborhood'),
12248 closedNeighborhood: function closedNeighborhood(selector) {
12249 return this.neighborhood().add(this).filter(selector);
12250 },
12251 openNeighborhood: function openNeighborhood(selector) {
12252 return this.neighborhood(selector);
12253 }
12254}); // aliases
12255
12256elesfn$t.neighbourhood = elesfn$t.neighborhood;
12257elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
12258elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
12259/////////////////
12260
12261extend(elesfn$t, {
12262 source: cache(function sourceImpl(selector) {
12263 var ele = this[0];
12264 var src;
12265
12266 if (ele) {
12267 src = ele._private.source || ele.cy().collection();
12268 }
12269
12270 return src && selector ? src.filter(selector) : src;
12271 }, 'source'),
12272 target: cache(function targetImpl(selector) {
12273 var ele = this[0];
12274 var tgt;
12275
12276 if (ele) {
12277 tgt = ele._private.target || ele.cy().collection();
12278 }
12279
12280 return tgt && selector ? tgt.filter(selector) : tgt;
12281 }, 'target'),
12282 sources: defineSourceFunction({
12283 attr: 'source'
12284 }),
12285 targets: defineSourceFunction({
12286 attr: 'target'
12287 })
12288});
12289
12290function defineSourceFunction(params) {
12291 return function sourceImpl(selector) {
12292 var sources = [];
12293
12294 for (var i = 0; i < this.length; i++) {
12295 var ele = this[i];
12296 var src = ele._private[params.attr];
12297
12298 if (src) {
12299 sources.push(src);
12300 }
12301 }
12302
12303 return this.spawn(sources, true).filter(selector);
12304 };
12305}
12306
12307extend(elesfn$t, {
12308 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
12309 edgesTo: cache(defineEdgesWithFunction({
12310 thisIsSrc: true
12311 }), 'edgesTo')
12312});
12313
12314function defineEdgesWithFunction(params) {
12315 return function edgesWithImpl(otherNodes) {
12316 var elements = [];
12317 var cy = this._private.cy;
12318 var p = params || {}; // get elements if a selector is specified
12319
12320 if (string(otherNodes)) {
12321 otherNodes = cy.$(otherNodes);
12322 }
12323
12324 for (var h = 0; h < otherNodes.length; h++) {
12325 var edges = otherNodes[h]._private.edges;
12326
12327 for (var i = 0; i < edges.length; i++) {
12328 var edge = edges[i];
12329 var edgeData = edge._private.data;
12330 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
12331 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
12332 var edgeConnectsThisAndOther = thisToOther || otherToThis;
12333
12334 if (!edgeConnectsThisAndOther) {
12335 continue;
12336 }
12337
12338 if (p.thisIsSrc || p.thisIsTgt) {
12339 if (p.thisIsSrc && !thisToOther) {
12340 continue;
12341 }
12342
12343 if (p.thisIsTgt && !otherToThis) {
12344 continue;
12345 }
12346 }
12347
12348 elements.push(edge);
12349 }
12350 }
12351
12352 return this.spawn(elements, true);
12353 };
12354}
12355
12356extend(elesfn$t, {
12357 connectedEdges: cache(function (selector) {
12358 var retEles = [];
12359 var eles = this;
12360
12361 for (var i = 0; i < eles.length; i++) {
12362 var node = eles[i];
12363
12364 if (!node.isNode()) {
12365 continue;
12366 }
12367
12368 var edges = node._private.edges;
12369
12370 for (var j = 0; j < edges.length; j++) {
12371 var edge = edges[j];
12372 retEles.push(edge);
12373 }
12374 }
12375
12376 return this.spawn(retEles, true).filter(selector);
12377 }, 'connectedEdges'),
12378 connectedNodes: cache(function (selector) {
12379 var retEles = [];
12380 var eles = this;
12381
12382 for (var i = 0; i < eles.length; i++) {
12383 var edge = eles[i];
12384
12385 if (!edge.isEdge()) {
12386 continue;
12387 }
12388
12389 retEles.push(edge.source()[0]);
12390 retEles.push(edge.target()[0]);
12391 }
12392
12393 return this.spawn(retEles, true).filter(selector);
12394 }, 'connectedNodes'),
12395 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
12396 codirectedEdges: cache(defineParallelEdgesFunction({
12397 codirected: true
12398 }), 'codirectedEdges')
12399});
12400
12401function defineParallelEdgesFunction(params) {
12402 var defaults = {
12403 codirected: false
12404 };
12405 params = extend({}, defaults, params);
12406 return function parallelEdgesImpl(selector) {
12407 // micro-optimised for renderer
12408 var elements = [];
12409 var edges = this.edges();
12410 var p = params; // look at all the edges in the collection
12411
12412 for (var i = 0; i < edges.length; i++) {
12413 var edge1 = edges[i];
12414 var edge1_p = edge1._private;
12415 var src1 = edge1_p.source;
12416 var srcid1 = src1._private.data.id;
12417 var tgtid1 = edge1_p.data.target;
12418 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
12419
12420 for (var j = 0; j < srcEdges1.length; j++) {
12421 var edge2 = srcEdges1[j];
12422 var edge2data = edge2._private.data;
12423 var tgtid2 = edge2data.target;
12424 var srcid2 = edge2data.source;
12425 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
12426 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
12427
12428 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
12429 elements.push(edge2);
12430 }
12431 }
12432 }
12433
12434 return this.spawn(elements, true).filter(selector);
12435 };
12436} // Misc functions
12437/////////////////
12438
12439
12440extend(elesfn$t, {
12441 components: function components(root) {
12442 var self = this;
12443 var cy = self.cy();
12444 var visited = cy.collection();
12445 var unvisited = root == null ? self.nodes() : root.nodes();
12446 var components = [];
12447
12448 if (root != null && unvisited.empty()) {
12449 // root may contain only edges
12450 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
12451 }
12452
12453 var visitInComponent = function visitInComponent(node, component) {
12454 visited.merge(node);
12455 unvisited.unmerge(node);
12456 component.merge(node);
12457 };
12458
12459 if (unvisited.empty()) {
12460 return self.spawn();
12461 }
12462
12463 var _loop = function _loop() {
12464 // each iteration yields a component
12465 var cmpt = cy.collection();
12466 components.push(cmpt);
12467 var root = unvisited[0];
12468 visitInComponent(root, cmpt);
12469 self.bfs({
12470 directed: false,
12471 roots: root,
12472 visit: function visit(v) {
12473 return visitInComponent(v, cmpt);
12474 }
12475 });
12476 cmpt.forEach(function (node) {
12477 node.connectedEdges().forEach(function (e) {
12478 // connectedEdges() usually cached
12479 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
12480 // has() is cheap
12481 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
12482 }
12483 });
12484 });
12485 };
12486
12487 do {
12488 _loop();
12489 } while (unvisited.length > 0);
12490
12491 return components;
12492 },
12493 component: function component() {
12494 var ele = this[0];
12495 return ele.cy().mutableElements().components(ele)[0];
12496 }
12497});
12498elesfn$t.componentsOf = elesfn$t.components;
12499
12500var Collection = function Collection(cy, elements) {
12501 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
12502
12503 if (cy === undefined) {
12504 error('A collection must have a reference to the core');
12505 return;
12506 }
12507
12508 var map = new Map$1();
12509 var createdElements = false;
12510
12511 if (!elements) {
12512 elements = [];
12513 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
12514 createdElements = true; // make elements from json and restore all at once later
12515
12516 var eles = [];
12517 var elesIds = new Set$1();
12518
12519 for (var i = 0, l = elements.length; i < l; i++) {
12520 var json = elements[i];
12521
12522 if (json.data == null) {
12523 json.data = {};
12524 }
12525
12526 var _data = json.data; // make sure newly created elements have valid ids
12527
12528 if (_data.id == null) {
12529 _data.id = uuid();
12530 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
12531 continue; // can't create element if prior id already exists
12532 }
12533
12534 var ele = new Element(cy, json, false);
12535 eles.push(ele);
12536 elesIds.add(_data.id);
12537 }
12538
12539 elements = eles;
12540 }
12541
12542 this.length = 0;
12543
12544 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
12545 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
12546
12547 if (element$1 == null) {
12548 continue;
12549 }
12550
12551 var id = element$1._private.data.id;
12552
12553 if (!unique || !map.has(id)) {
12554 if (unique) {
12555 map.set(id, {
12556 index: this.length,
12557 ele: element$1
12558 });
12559 }
12560
12561 this[this.length] = element$1;
12562 this.length++;
12563 }
12564 }
12565
12566 this._private = {
12567 eles: this,
12568 cy: cy,
12569
12570 get map() {
12571 if (this.lazyMap == null) {
12572 this.rebuildMap();
12573 }
12574
12575 return this.lazyMap;
12576 },
12577
12578 set map(m) {
12579 this.lazyMap = m;
12580 },
12581
12582 rebuildMap: function rebuildMap() {
12583 var m = this.lazyMap = new Map$1();
12584 var eles = this.eles;
12585
12586 for (var _i2 = 0; _i2 < eles.length; _i2++) {
12587 var _ele = eles[_i2];
12588 m.set(_ele.id(), {
12589 index: _i2,
12590 ele: _ele
12591 });
12592 }
12593 }
12594 };
12595
12596 if (unique) {
12597 this._private.map = map;
12598 } // restore the elements if we created them from json
12599
12600
12601 if (createdElements) {
12602 this.restore();
12603 }
12604}; // Functions
12605////////////////////////////////////////////////////////////////////////////////////////////////////
12606// keep the prototypes in sync (an element has the same functions as a collection)
12607// and use elefn and elesfn as shorthands to the prototypes
12608
12609
12610var elesfn$u = Element.prototype = Collection.prototype = Object.create(Array.prototype);
12611
12612elesfn$u.instanceString = function () {
12613 return 'collection';
12614};
12615
12616elesfn$u.spawn = function (eles, unique) {
12617 return new Collection(this.cy(), eles, unique);
12618};
12619
12620elesfn$u.spawnSelf = function () {
12621 return this.spawn(this);
12622};
12623
12624elesfn$u.cy = function () {
12625 return this._private.cy;
12626};
12627
12628elesfn$u.renderer = function () {
12629 return this._private.cy.renderer();
12630};
12631
12632elesfn$u.element = function () {
12633 return this[0];
12634};
12635
12636elesfn$u.collection = function () {
12637 if (collection(this)) {
12638 return this;
12639 } else {
12640 // an element
12641 return new Collection(this._private.cy, [this]);
12642 }
12643};
12644
12645elesfn$u.unique = function () {
12646 return new Collection(this._private.cy, this, true);
12647};
12648
12649elesfn$u.hasElementWithId = function (id) {
12650 id = '' + id; // id must be string
12651
12652 return this._private.map.has(id);
12653};
12654
12655elesfn$u.getElementById = function (id) {
12656 id = '' + id; // id must be string
12657
12658 var cy = this._private.cy;
12659
12660 var entry = this._private.map.get(id);
12661
12662 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
12663};
12664
12665elesfn$u.$id = elesfn$u.getElementById;
12666
12667elesfn$u.poolIndex = function () {
12668 var cy = this._private.cy;
12669 var eles = cy._private.elements;
12670 var id = this[0]._private.data.id;
12671 return eles._private.map.get(id).index;
12672};
12673
12674elesfn$u.indexOf = function (ele) {
12675 var id = ele[0]._private.data.id;
12676 return this._private.map.get(id).index;
12677};
12678
12679elesfn$u.indexOfId = function (id) {
12680 id = '' + id; // id must be string
12681
12682 return this._private.map.get(id).index;
12683};
12684
12685elesfn$u.json = function (obj) {
12686 var ele = this.element();
12687 var cy = this.cy();
12688
12689 if (ele == null && obj) {
12690 return this;
12691 } // can't set to no eles
12692
12693
12694 if (ele == null) {
12695 return undefined;
12696 } // can't get from no eles
12697
12698
12699 var p = ele._private;
12700
12701 if (plainObject(obj)) {
12702 // set
12703 cy.startBatch();
12704
12705 if (obj.data) {
12706 ele.data(obj.data);
12707 var _data2 = p.data;
12708
12709 if (ele.isEdge()) {
12710 // source and target are immutable via data()
12711 var move = false;
12712 var spec = {};
12713 var src = obj.data.source;
12714 var tgt = obj.data.target;
12715
12716 if (src != null && src != _data2.source) {
12717 spec.source = '' + src; // id must be string
12718
12719 move = true;
12720 }
12721
12722 if (tgt != null && tgt != _data2.target) {
12723 spec.target = '' + tgt; // id must be string
12724
12725 move = true;
12726 }
12727
12728 if (move) {
12729 ele = ele.move(spec);
12730 }
12731 } else {
12732 // parent is immutable via data()
12733 var newParentValSpecd = 'parent' in obj.data;
12734 var parent = obj.data.parent;
12735
12736 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
12737 if (parent === undefined) {
12738 // can't set undefined imperatively, so use null
12739 parent = null;
12740 }
12741
12742 if (parent != null) {
12743 parent = '' + parent; // id must be string
12744 }
12745
12746 ele = ele.move({
12747 parent: parent
12748 });
12749 }
12750 }
12751 }
12752
12753 if (obj.position) {
12754 ele.position(obj.position);
12755 } // ignore group -- immutable
12756
12757
12758 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
12759 var obj_k = obj[k];
12760
12761 if (obj_k != null && obj_k !== p[k]) {
12762 if (obj_k) {
12763 ele[trueFnName]();
12764 } else {
12765 ele[falseFnName]();
12766 }
12767 }
12768 };
12769
12770 checkSwitch('removed', 'remove', 'restore');
12771 checkSwitch('selected', 'select', 'unselect');
12772 checkSwitch('selectable', 'selectify', 'unselectify');
12773 checkSwitch('locked', 'lock', 'unlock');
12774 checkSwitch('grabbable', 'grabify', 'ungrabify');
12775 checkSwitch('pannable', 'panify', 'unpanify');
12776
12777 if (obj.classes != null) {
12778 ele.classes(obj.classes);
12779 }
12780
12781 cy.endBatch();
12782 return this;
12783 } else if (obj === undefined) {
12784 // get
12785 var json = {
12786 data: copy(p.data),
12787 position: copy(p.position),
12788 group: p.group,
12789 removed: p.removed,
12790 selected: p.selected,
12791 selectable: p.selectable,
12792 locked: p.locked,
12793 grabbable: p.grabbable,
12794 pannable: p.pannable,
12795 classes: null
12796 };
12797 json.classes = '';
12798 var i = 0;
12799 p.classes.forEach(function (cls) {
12800 return json.classes += i++ === 0 ? cls : ' ' + cls;
12801 });
12802 return json;
12803 }
12804};
12805
12806elesfn$u.jsons = function () {
12807 var jsons = [];
12808
12809 for (var i = 0; i < this.length; i++) {
12810 var ele = this[i];
12811 var json = ele.json();
12812 jsons.push(json);
12813 }
12814
12815 return jsons;
12816};
12817
12818elesfn$u.clone = function () {
12819 var cy = this.cy();
12820 var elesArr = [];
12821
12822 for (var i = 0; i < this.length; i++) {
12823 var ele = this[i];
12824 var json = ele.json();
12825 var clone = new Element(cy, json, false); // NB no restore
12826
12827 elesArr.push(clone);
12828 }
12829
12830 return new Collection(cy, elesArr);
12831};
12832
12833elesfn$u.copy = elesfn$u.clone;
12834
12835elesfn$u.restore = function () {
12836 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12837 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12838 var self = this;
12839 var cy = self.cy();
12840 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
12841 // restore the nodes first
12842
12843 var nodes = [];
12844 var edges = [];
12845 var elements;
12846
12847 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
12848 var ele = self[_i3];
12849
12850 if (addToPool && !ele.removed()) {
12851 // don't need to handle this ele
12852 continue;
12853 } // keep nodes first in the array and edges after
12854
12855
12856 if (ele.isNode()) {
12857 // put to front of array if node
12858 nodes.push(ele);
12859 } else {
12860 // put to end of array if edge
12861 edges.push(ele);
12862 }
12863 }
12864
12865 elements = nodes.concat(edges);
12866 var i;
12867
12868 var removeFromElements = function removeFromElements() {
12869 elements.splice(i, 1);
12870 i--;
12871 }; // now, restore each element
12872
12873
12874 for (i = 0; i < elements.length; i++) {
12875 var _ele2 = elements[i];
12876 var _private = _ele2._private;
12877 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
12878
12879 _ele2.clearTraversalCache(); // set id and validate
12880
12881
12882 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
12883 _data3.id = uuid();
12884 } else if (number(_data3.id)) {
12885 _data3.id = '' + _data3.id; // now it's a string
12886 } else if (emptyString(_data3.id) || !string(_data3.id)) {
12887 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
12888
12889 removeFromElements();
12890 continue;
12891 } else if (cy.hasElementWithId(_data3.id)) {
12892 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
12893
12894 removeFromElements();
12895 continue;
12896 }
12897
12898 var id = _data3.id; // id is finalised, now let's keep a ref
12899
12900 if (_ele2.isNode()) {
12901 // extra checks for nodes
12902 var pos = _private.position; // make sure the nodes have a defined position
12903
12904 if (pos.x == null) {
12905 pos.x = 0;
12906 }
12907
12908 if (pos.y == null) {
12909 pos.y = 0;
12910 }
12911 }
12912
12913 if (_ele2.isEdge()) {
12914 // extra checks for edges
12915 var edge = _ele2;
12916 var fields = ['source', 'target'];
12917 var fieldsLength = fields.length;
12918 var badSourceOrTarget = false;
12919
12920 for (var j = 0; j < fieldsLength; j++) {
12921 var field = fields[j];
12922 var val = _data3[field];
12923
12924 if (number(val)) {
12925 val = _data3[field] = '' + _data3[field]; // now string
12926 }
12927
12928 if (val == null || val === '') {
12929 // can't create if source or target is not defined properly
12930 error('Can not create edge `' + id + '` with unspecified ' + field);
12931 badSourceOrTarget = true;
12932 } else if (!cy.hasElementWithId(val)) {
12933 // can't create edge if one of its nodes doesn't exist
12934 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
12935 badSourceOrTarget = true;
12936 }
12937 }
12938
12939 if (badSourceOrTarget) {
12940 removeFromElements();
12941 continue;
12942 } // can't create this
12943
12944
12945 var src = cy.getElementById(_data3.source);
12946 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
12947
12948 if (src.same(tgt)) {
12949 src._private.edges.push(edge);
12950 } else {
12951 src._private.edges.push(edge);
12952
12953 tgt._private.edges.push(edge);
12954 }
12955
12956 edge._private.source = src;
12957 edge._private.target = tgt;
12958 } // if is edge
12959 // create mock ids / indexes maps for element so it can be used like collections
12960
12961
12962 _private.map = new Map$1();
12963
12964 _private.map.set(id, {
12965 ele: _ele2,
12966 index: 0
12967 });
12968
12969 _private.removed = false;
12970
12971 if (addToPool) {
12972 cy.addToPool(_ele2);
12973 }
12974 } // for each element
12975 // do compound node sanity checks
12976
12977
12978 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
12979 // each node
12980 var node = nodes[_i4];
12981 var _data4 = node._private.data;
12982
12983 if (number(_data4.parent)) {
12984 // then automake string
12985 _data4.parent = '' + _data4.parent;
12986 }
12987
12988 var parentId = _data4.parent;
12989 var specifiedParent = parentId != null;
12990
12991 if (specifiedParent) {
12992 var parent = cy.getElementById(parentId);
12993
12994 if (parent.empty()) {
12995 // non-existant parent; just remove it
12996 _data4.parent = undefined;
12997 } else {
12998 var selfAsParent = false;
12999 var ancestor = parent;
13000
13001 while (!ancestor.empty()) {
13002 if (node.same(ancestor)) {
13003 // mark self as parent and remove from data
13004 selfAsParent = true;
13005 _data4.parent = undefined; // remove parent reference
13006 // exit or we loop forever
13007
13008 break;
13009 }
13010
13011 ancestor = ancestor.parent();
13012 }
13013
13014 if (!selfAsParent) {
13015 // connect with children
13016 parent[0]._private.children.push(node);
13017
13018 node._private.parent = parent[0]; // let the core know we have a compound graph
13019
13020 cy_p.hasCompoundNodes = true;
13021 }
13022 } // else
13023
13024 } // if specified parent
13025
13026 } // for each node
13027
13028
13029 if (elements.length > 0) {
13030 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13031
13032 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13033 var _ele3 = restored[_i5];
13034
13035 if (_ele3.isNode()) {
13036 continue;
13037 } // adding an edge invalidates the traversal caches for the parallel edges
13038
13039
13040 _ele3.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13041
13042
13043 _ele3.source().clearTraversalCache();
13044
13045 _ele3.target().clearTraversalCache();
13046 }
13047
13048 var toUpdateStyle;
13049
13050 if (cy_p.hasCompoundNodes) {
13051 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13052 } else {
13053 toUpdateStyle = restored;
13054 }
13055
13056 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13057
13058 if (notifyRenderer) {
13059 restored.emitAndNotify('add');
13060 } else if (addToPool) {
13061 restored.emit('add');
13062 }
13063 }
13064
13065 return self; // chainability
13066};
13067
13068elesfn$u.removed = function () {
13069 var ele = this[0];
13070 return ele && ele._private.removed;
13071};
13072
13073elesfn$u.inside = function () {
13074 var ele = this[0];
13075 return ele && !ele._private.removed;
13076};
13077
13078elesfn$u.remove = function () {
13079 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13080 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13081 var self = this;
13082 var elesToRemove = [];
13083 var elesToRemoveIds = {};
13084 var cy = self._private.cy; // add connected edges
13085
13086 function addConnectedEdges(node) {
13087 var edges = node._private.edges;
13088
13089 for (var i = 0; i < edges.length; i++) {
13090 add(edges[i]);
13091 }
13092 } // add descendant nodes
13093
13094
13095 function addChildren(node) {
13096 var children = node._private.children;
13097
13098 for (var i = 0; i < children.length; i++) {
13099 add(children[i]);
13100 }
13101 }
13102
13103 function add(ele) {
13104 var alreadyAdded = elesToRemoveIds[ele.id()];
13105
13106 if (removeFromPool && ele.removed() || alreadyAdded) {
13107 return;
13108 } else {
13109 elesToRemoveIds[ele.id()] = true;
13110 }
13111
13112 if (ele.isNode()) {
13113 elesToRemove.push(ele); // nodes are removed last
13114
13115 addConnectedEdges(ele);
13116 addChildren(ele);
13117 } else {
13118 elesToRemove.unshift(ele); // edges are removed first
13119 }
13120 } // make the list of elements to remove
13121 // (may be removing more than specified due to connected edges etc)
13122
13123
13124 for (var i = 0, l = self.length; i < l; i++) {
13125 var ele = self[i];
13126 add(ele);
13127 }
13128
13129 function removeEdgeRef(node, edge) {
13130 var connectedEdges = node._private.edges;
13131 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13132
13133 node.clearTraversalCache();
13134 }
13135
13136 function removeParallelRef(pllEdge) {
13137 // removing an edge invalidates the traversal caches for the parallel edges
13138 pllEdge.clearTraversalCache();
13139 }
13140
13141 var alteredParents = [];
13142 alteredParents.ids = {};
13143
13144 function removeChildRef(parent, ele) {
13145 ele = ele[0];
13146 parent = parent[0];
13147 var children = parent._private.children;
13148 var pid = parent.id();
13149 removeFromArray(children, ele); // remove parent => child ref
13150
13151 ele._private.parent = null; // remove child => parent ref
13152
13153 if (!alteredParents.ids[pid]) {
13154 alteredParents.ids[pid] = true;
13155 alteredParents.push(parent);
13156 }
13157 }
13158
13159 self.dirtyCompoundBoundsCache();
13160
13161 if (removeFromPool) {
13162 cy.removeFromPool(elesToRemove); // remove from core pool
13163 }
13164
13165 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13166 var _ele4 = elesToRemove[_i6];
13167
13168 if (_ele4.isEdge()) {
13169 // remove references to this edge in its connected nodes
13170 var src = _ele4.source()[0];
13171
13172 var tgt = _ele4.target()[0];
13173
13174 removeEdgeRef(src, _ele4);
13175 removeEdgeRef(tgt, _ele4);
13176
13177 var pllEdges = _ele4.parallelEdges();
13178
13179 for (var j = 0; j < pllEdges.length; j++) {
13180 var pllEdge = pllEdges[j];
13181 removeParallelRef(pllEdge);
13182
13183 if (pllEdge.isBundledBezier()) {
13184 pllEdge.dirtyBoundingBoxCache();
13185 }
13186 }
13187 } else {
13188 // remove reference to parent
13189 var parent = _ele4.parent();
13190
13191 if (parent.length !== 0) {
13192 removeChildRef(parent, _ele4);
13193 }
13194 }
13195
13196 if (removeFromPool) {
13197 // mark as removed
13198 _ele4._private.removed = true;
13199 }
13200 } // check to see if we have a compound graph or not
13201
13202
13203 var elesStillInside = cy._private.elements;
13204 cy._private.hasCompoundNodes = false;
13205
13206 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13207 var _ele5 = elesStillInside[_i7];
13208
13209 if (_ele5.isParent()) {
13210 cy._private.hasCompoundNodes = true;
13211 break;
13212 }
13213 }
13214
13215 var removedElements = new Collection(this.cy(), elesToRemove);
13216
13217 if (removedElements.size() > 0) {
13218 // must manually notify since trigger won't do this automatically once removed
13219 if (notifyRenderer) {
13220 removedElements.emitAndNotify('remove');
13221 } else if (removeFromPool) {
13222 removedElements.emit('remove');
13223 }
13224 } // the parents who were modified by the removal need their style updated
13225
13226
13227 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13228 var _ele6 = alteredParents[_i8];
13229
13230 if (!removeFromPool || !_ele6.removed()) {
13231 _ele6.updateStyle();
13232 }
13233 }
13234
13235 return removedElements;
13236};
13237
13238elesfn$u.move = function (struct) {
13239 var cy = this._private.cy;
13240 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
13241 // (our calls to remove/restore do not remove from the graph or make events)
13242
13243 var notifyRenderer = false;
13244 var modifyPool = false;
13245
13246 var toString = function toString(id) {
13247 return id == null ? id : '' + id;
13248 }; // id must be string
13249
13250
13251 if (struct.source !== undefined || struct.target !== undefined) {
13252 var srcId = toString(struct.source);
13253 var tgtId = toString(struct.target);
13254 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13255 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13256
13257 if (srcExists || tgtExists) {
13258 cy.batch(function () {
13259 // avoid duplicate style updates
13260 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13261
13262 eles.emitAndNotify('moveout');
13263
13264 for (var i = 0; i < eles.length; i++) {
13265 var ele = eles[i];
13266 var _data5 = ele._private.data;
13267
13268 if (ele.isEdge()) {
13269 if (srcExists) {
13270 _data5.source = srcId;
13271 }
13272
13273 if (tgtExists) {
13274 _data5.target = tgtId;
13275 }
13276 }
13277 }
13278
13279 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13280 });
13281 eles.emitAndNotify('move');
13282 }
13283 } else if (struct.parent !== undefined) {
13284 // move node to new parent
13285 var parentId = toString(struct.parent);
13286 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13287
13288 if (parentExists) {
13289 var pidToAssign = parentId === null ? undefined : parentId;
13290 cy.batch(function () {
13291 // avoid duplicate style updates
13292 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13293
13294 updated.emitAndNotify('moveout');
13295
13296 for (var i = 0; i < eles.length; i++) {
13297 var ele = eles[i];
13298 var _data6 = ele._private.data;
13299
13300 if (ele.isNode()) {
13301 _data6.parent = pidToAssign;
13302 }
13303 }
13304
13305 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13306 });
13307 eles.emitAndNotify('move');
13308 }
13309 }
13310
13311 return this;
13312};
13313
13314[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) {
13315 extend(elesfn$u, props);
13316});
13317
13318var corefn = {
13319 add: function add(opts) {
13320 var elements;
13321 var cy = this; // add the elements
13322
13323 if (elementOrCollection(opts)) {
13324 var eles = opts;
13325
13326 if (eles._private.cy === cy) {
13327 // same instance => just restore
13328 elements = eles.restore();
13329 } else {
13330 // otherwise, copy from json
13331 var jsons = [];
13332
13333 for (var i = 0; i < eles.length; i++) {
13334 var ele = eles[i];
13335 jsons.push(ele.json());
13336 }
13337
13338 elements = new Collection(cy, jsons);
13339 }
13340 } // specify an array of options
13341 else if (array(opts)) {
13342 var _jsons = opts;
13343 elements = new Collection(cy, _jsons);
13344 } // specify via opts.nodes and opts.edges
13345 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13346 var elesByGroup = opts;
13347 var _jsons2 = [];
13348 var grs = ['nodes', 'edges'];
13349
13350 for (var _i = 0, il = grs.length; _i < il; _i++) {
13351 var group = grs[_i];
13352 var elesArray = elesByGroup[group];
13353
13354 if (array(elesArray)) {
13355 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13356 var json = extend({
13357 group: group
13358 }, elesArray[j]);
13359
13360 _jsons2.push(json);
13361 }
13362 }
13363 }
13364
13365 elements = new Collection(cy, _jsons2);
13366 } // specify options for one element
13367 else {
13368 var _json = opts;
13369 elements = new Element(cy, _json).collection();
13370 }
13371
13372 return elements;
13373 },
13374 remove: function remove(collection) {
13375 if (elementOrCollection(collection)) ; else if (string(collection)) {
13376 var selector = collection;
13377 collection = this.$(selector);
13378 }
13379
13380 return collection.remove();
13381 }
13382};
13383
13384/* global Float32Array */
13385
13386/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13387function generateCubicBezier(mX1, mY1, mX2, mY2) {
13388 var NEWTON_ITERATIONS = 4,
13389 NEWTON_MIN_SLOPE = 0.001,
13390 SUBDIVISION_PRECISION = 0.0000001,
13391 SUBDIVISION_MAX_ITERATIONS = 10,
13392 kSplineTableSize = 11,
13393 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
13394 float32ArraySupported = typeof Float32Array !== 'undefined';
13395 /* Must contain four arguments. */
13396
13397 if (arguments.length !== 4) {
13398 return false;
13399 }
13400 /* Arguments must be numbers. */
13401
13402
13403 for (var i = 0; i < 4; ++i) {
13404 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
13405 return false;
13406 }
13407 }
13408 /* X values must be in the [0, 1] range. */
13409
13410
13411 mX1 = Math.min(mX1, 1);
13412 mX2 = Math.min(mX2, 1);
13413 mX1 = Math.max(mX1, 0);
13414 mX2 = Math.max(mX2, 0);
13415 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
13416
13417 function A(aA1, aA2) {
13418 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
13419 }
13420
13421 function B(aA1, aA2) {
13422 return 3.0 * aA2 - 6.0 * aA1;
13423 }
13424
13425 function C(aA1) {
13426 return 3.0 * aA1;
13427 }
13428
13429 function calcBezier(aT, aA1, aA2) {
13430 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
13431 }
13432
13433 function getSlope(aT, aA1, aA2) {
13434 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
13435 }
13436
13437 function newtonRaphsonIterate(aX, aGuessT) {
13438 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
13439 var currentSlope = getSlope(aGuessT, mX1, mX2);
13440
13441 if (currentSlope === 0.0) {
13442 return aGuessT;
13443 }
13444
13445 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
13446 aGuessT -= currentX / currentSlope;
13447 }
13448
13449 return aGuessT;
13450 }
13451
13452 function calcSampleValues() {
13453 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
13454 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
13455 }
13456 }
13457
13458 function binarySubdivide(aX, aA, aB) {
13459 var currentX,
13460 currentT,
13461 i = 0;
13462
13463 do {
13464 currentT = aA + (aB - aA) / 2.0;
13465 currentX = calcBezier(currentT, mX1, mX2) - aX;
13466
13467 if (currentX > 0.0) {
13468 aB = currentT;
13469 } else {
13470 aA = currentT;
13471 }
13472 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
13473
13474 return currentT;
13475 }
13476
13477 function getTForX(aX) {
13478 var intervalStart = 0.0,
13479 currentSample = 1,
13480 lastSample = kSplineTableSize - 1;
13481
13482 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
13483 intervalStart += kSampleStepSize;
13484 }
13485
13486 --currentSample;
13487 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
13488 guessForT = intervalStart + dist * kSampleStepSize,
13489 initialSlope = getSlope(guessForT, mX1, mX2);
13490
13491 if (initialSlope >= NEWTON_MIN_SLOPE) {
13492 return newtonRaphsonIterate(aX, guessForT);
13493 } else if (initialSlope === 0.0) {
13494 return guessForT;
13495 } else {
13496 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
13497 }
13498 }
13499
13500 var _precomputed = false;
13501
13502 function precompute() {
13503 _precomputed = true;
13504
13505 if (mX1 !== mY1 || mX2 !== mY2) {
13506 calcSampleValues();
13507 }
13508 }
13509
13510 var f = function f(aX) {
13511 if (!_precomputed) {
13512 precompute();
13513 }
13514
13515 if (mX1 === mY1 && mX2 === mY2) {
13516 return aX;
13517 }
13518
13519 if (aX === 0) {
13520 return 0;
13521 }
13522
13523 if (aX === 1) {
13524 return 1;
13525 }
13526
13527 return calcBezier(getTForX(aX), mY1, mY2);
13528 };
13529
13530 f.getControlPoints = function () {
13531 return [{
13532 x: mX1,
13533 y: mY1
13534 }, {
13535 x: mX2,
13536 y: mY2
13537 }];
13538 };
13539
13540 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
13541
13542 f.toString = function () {
13543 return str;
13544 };
13545
13546 return f;
13547}
13548
13549/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13550
13551/* 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
13552 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
13553var generateSpringRK4 = function () {
13554 function springAccelerationForState(state) {
13555 return -state.tension * state.x - state.friction * state.v;
13556 }
13557
13558 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
13559 var state = {
13560 x: initialState.x + derivative.dx * dt,
13561 v: initialState.v + derivative.dv * dt,
13562 tension: initialState.tension,
13563 friction: initialState.friction
13564 };
13565 return {
13566 dx: state.v,
13567 dv: springAccelerationForState(state)
13568 };
13569 }
13570
13571 function springIntegrateState(state, dt) {
13572 var a = {
13573 dx: state.v,
13574 dv: springAccelerationForState(state)
13575 },
13576 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
13577 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
13578 d = springEvaluateStateWithDerivative(state, dt, c),
13579 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
13580 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
13581 state.x = state.x + dxdt * dt;
13582 state.v = state.v + dvdt * dt;
13583 return state;
13584 }
13585
13586 return function springRK4Factory(tension, friction, duration) {
13587 var initState = {
13588 x: -1,
13589 v: 0,
13590 tension: null,
13591 friction: null
13592 },
13593 path = [0],
13594 time_lapsed = 0,
13595 tolerance = 1 / 10000,
13596 DT = 16 / 1000,
13597 have_duration,
13598 dt,
13599 last_state;
13600 tension = parseFloat(tension) || 500;
13601 friction = parseFloat(friction) || 20;
13602 duration = duration || null;
13603 initState.tension = tension;
13604 initState.friction = friction;
13605 have_duration = duration !== null;
13606 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
13607
13608 if (have_duration) {
13609 /* Run the simulation without a duration. */
13610 time_lapsed = springRK4Factory(tension, friction);
13611 /* Compute the adjusted time delta. */
13612
13613 dt = time_lapsed / duration * DT;
13614 } else {
13615 dt = DT;
13616 }
13617
13618 for (;;) {
13619 /* Next/step function .*/
13620 last_state = springIntegrateState(last_state || initState, dt);
13621 /* Store the position. */
13622
13623 path.push(1 + last_state.x);
13624 time_lapsed += 16;
13625 /* If the change threshold is reached, break. */
13626
13627 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
13628 break;
13629 }
13630 }
13631 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
13632 computed path and returns a snapshot of the position according to a given percentComplete. */
13633
13634
13635 return !have_duration ? time_lapsed : function (percentComplete) {
13636 return path[percentComplete * (path.length - 1) | 0];
13637 };
13638 };
13639}();
13640
13641var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
13642 var bezier = generateCubicBezier(t1, p1, t2, p2);
13643 return function (start, end, percent) {
13644 return start + (end - start) * bezier(percent);
13645 };
13646};
13647
13648var easings = {
13649 'linear': function linear(start, end, percent) {
13650 return start + (end - start) * percent;
13651 },
13652 // default easings
13653 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
13654 'ease-in': cubicBezier(0.42, 0, 1, 1),
13655 'ease-out': cubicBezier(0, 0, 0.58, 1),
13656 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
13657 // sine
13658 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
13659 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
13660 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
13661 // quad
13662 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
13663 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
13664 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
13665 // cubic
13666 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
13667 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
13668 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
13669 // quart
13670 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
13671 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
13672 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
13673 // quint
13674 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
13675 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
13676 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
13677 // expo
13678 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
13679 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
13680 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
13681 // circ
13682 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
13683 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
13684 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
13685 // user param easings...
13686 'spring': function spring(tension, friction, duration) {
13687 if (duration === 0) {
13688 // can't get a spring w/ duration 0
13689 return easings.linear; // duration 0 => jump to end so impl doesn't matter
13690 }
13691
13692 var spring = generateSpringRK4(tension, friction, duration);
13693 return function (start, end, percent) {
13694 return start + (end - start) * spring(percent);
13695 };
13696 },
13697 'cubic-bezier': cubicBezier
13698};
13699
13700function getEasedValue(type, start, end, percent, easingFn) {
13701 if (percent === 1) {
13702 return end;
13703 }
13704
13705 if (start === end) {
13706 return end;
13707 }
13708
13709 var val = easingFn(start, end, percent);
13710
13711 if (type == null) {
13712 return val;
13713 }
13714
13715 if (type.roundValue || type.color) {
13716 val = Math.round(val);
13717 }
13718
13719 if (type.min !== undefined) {
13720 val = Math.max(val, type.min);
13721 }
13722
13723 if (type.max !== undefined) {
13724 val = Math.min(val, type.max);
13725 }
13726
13727 return val;
13728}
13729
13730function getValue(prop, spec) {
13731 if (prop.pfValue != null || prop.value != null) {
13732 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
13733 return prop.pfValue;
13734 } else {
13735 return prop.value;
13736 }
13737 } else {
13738 return prop;
13739 }
13740}
13741
13742function ease(startProp, endProp, percent, easingFn, propSpec) {
13743 var type = propSpec != null ? propSpec.type : null;
13744
13745 if (percent < 0) {
13746 percent = 0;
13747 } else if (percent > 1) {
13748 percent = 1;
13749 }
13750
13751 var start = getValue(startProp, propSpec);
13752 var end = getValue(endProp, propSpec);
13753
13754 if (number(start) && number(end)) {
13755 return getEasedValue(type, start, end, percent, easingFn);
13756 } else if (array(start) && array(end)) {
13757 var easedArr = [];
13758
13759 for (var i = 0; i < end.length; i++) {
13760 var si = start[i];
13761 var ei = end[i];
13762
13763 if (si != null && ei != null) {
13764 var val = getEasedValue(type, si, ei, percent, easingFn);
13765 easedArr.push(val);
13766 } else {
13767 easedArr.push(ei);
13768 }
13769 }
13770
13771 return easedArr;
13772 }
13773
13774 return undefined;
13775}
13776
13777function step(self, ani, now, isCore) {
13778 var isEles = !isCore;
13779 var _p = self._private;
13780 var ani_p = ani._private;
13781 var pEasing = ani_p.easing;
13782 var startTime = ani_p.startTime;
13783 var cy = isCore ? self : self.cy();
13784 var style = cy.style();
13785
13786 if (!ani_p.easingImpl) {
13787 if (pEasing == null) {
13788 // use default
13789 ani_p.easingImpl = easings['linear'];
13790 } else {
13791 // then define w/ name
13792 var easingVals;
13793
13794 if (string(pEasing)) {
13795 var easingProp = style.parse('transition-timing-function', pEasing);
13796 easingVals = easingProp.value;
13797 } else {
13798 // then assume preparsed array
13799 easingVals = pEasing;
13800 }
13801
13802 var name, args;
13803
13804 if (string(easingVals)) {
13805 name = easingVals;
13806 args = [];
13807 } else {
13808 name = easingVals[1];
13809 args = easingVals.slice(2).map(function (n) {
13810 return +n;
13811 });
13812 }
13813
13814 if (args.length > 0) {
13815 // create with args
13816 if (name === 'spring') {
13817 args.push(ani_p.duration); // need duration to generate spring
13818 }
13819
13820 ani_p.easingImpl = easings[name].apply(null, args);
13821 } else {
13822 // static impl by name
13823 ani_p.easingImpl = easings[name];
13824 }
13825 }
13826 }
13827
13828 var easing = ani_p.easingImpl;
13829 var percent;
13830
13831 if (ani_p.duration === 0) {
13832 percent = 1;
13833 } else {
13834 percent = (now - startTime) / ani_p.duration;
13835 }
13836
13837 if (ani_p.applying) {
13838 percent = ani_p.progress;
13839 }
13840
13841 if (percent < 0) {
13842 percent = 0;
13843 } else if (percent > 1) {
13844 percent = 1;
13845 }
13846
13847 if (ani_p.delay == null) {
13848 // then update
13849 var startPos = ani_p.startPosition;
13850 var endPos = ani_p.position;
13851
13852 if (endPos && isEles && !self.locked()) {
13853 var newPos = {};
13854
13855 if (valid(startPos.x, endPos.x)) {
13856 newPos.x = ease(startPos.x, endPos.x, percent, easing);
13857 }
13858
13859 if (valid(startPos.y, endPos.y)) {
13860 newPos.y = ease(startPos.y, endPos.y, percent, easing);
13861 }
13862
13863 self.position(newPos);
13864 }
13865
13866 var startPan = ani_p.startPan;
13867 var endPan = ani_p.pan;
13868 var pan = _p.pan;
13869 var animatingPan = endPan != null && isCore;
13870
13871 if (animatingPan) {
13872 if (valid(startPan.x, endPan.x)) {
13873 pan.x = ease(startPan.x, endPan.x, percent, easing);
13874 }
13875
13876 if (valid(startPan.y, endPan.y)) {
13877 pan.y = ease(startPan.y, endPan.y, percent, easing);
13878 }
13879
13880 self.emit('pan');
13881 }
13882
13883 var startZoom = ani_p.startZoom;
13884 var endZoom = ani_p.zoom;
13885 var animatingZoom = endZoom != null && isCore;
13886
13887 if (animatingZoom) {
13888 if (valid(startZoom, endZoom)) {
13889 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
13890 }
13891
13892 self.emit('zoom');
13893 }
13894
13895 if (animatingPan || animatingZoom) {
13896 self.emit('viewport');
13897 }
13898
13899 var props = ani_p.style;
13900
13901 if (props && props.length > 0 && isEles) {
13902 for (var i = 0; i < props.length; i++) {
13903 var prop = props[i];
13904 var _name = prop.name;
13905 var end = prop;
13906 var start = ani_p.startStyle[_name];
13907 var propSpec = style.properties[start.name];
13908 var easedVal = ease(start, end, percent, easing, propSpec);
13909 style.overrideBypass(self, _name, easedVal);
13910 } // for props
13911
13912
13913 self.emit('style');
13914 } // if
13915
13916 }
13917
13918 ani_p.progress = percent;
13919 return percent;
13920}
13921
13922function valid(start, end) {
13923 if (start == null || end == null) {
13924 return false;
13925 }
13926
13927 if (number(start) && number(end)) {
13928 return true;
13929 } else if (start && end) {
13930 return true;
13931 }
13932
13933 return false;
13934}
13935
13936function startAnimation(self, ani, now, isCore) {
13937 var ani_p = ani._private;
13938 ani_p.started = true;
13939 ani_p.startTime = now - ani_p.progress * ani_p.duration;
13940}
13941
13942function stepAll(now, cy) {
13943 var eles = cy._private.aniEles;
13944 var doneEles = [];
13945
13946 function stepOne(ele, isCore) {
13947 var _p = ele._private;
13948 var current = _p.animation.current;
13949 var queue = _p.animation.queue;
13950 var ranAnis = false; // if nothing currently animating, get something from the queue
13951
13952 if (current.length === 0) {
13953 var next = queue.shift();
13954
13955 if (next) {
13956 current.push(next);
13957 }
13958 }
13959
13960 var callbacks = function callbacks(_callbacks) {
13961 for (var j = _callbacks.length - 1; j >= 0; j--) {
13962 var cb = _callbacks[j];
13963 cb();
13964 }
13965
13966 _callbacks.splice(0, _callbacks.length);
13967 }; // step and remove if done
13968
13969
13970 for (var i = current.length - 1; i >= 0; i--) {
13971 var ani = current[i];
13972 var ani_p = ani._private;
13973
13974 if (ani_p.stopped) {
13975 current.splice(i, 1);
13976 ani_p.hooked = false;
13977 ani_p.playing = false;
13978 ani_p.started = false;
13979 callbacks(ani_p.frames);
13980 continue;
13981 }
13982
13983 if (!ani_p.playing && !ani_p.applying) {
13984 continue;
13985 } // an apply() while playing shouldn't do anything
13986
13987
13988 if (ani_p.playing && ani_p.applying) {
13989 ani_p.applying = false;
13990 }
13991
13992 if (!ani_p.started) {
13993 startAnimation(ele, ani, now);
13994 }
13995
13996 step(ele, ani, now, isCore);
13997
13998 if (ani_p.applying) {
13999 ani_p.applying = false;
14000 }
14001
14002 callbacks(ani_p.frames);
14003
14004 if (ani_p.step != null) {
14005 ani_p.step(now);
14006 }
14007
14008 if (ani.completed()) {
14009 current.splice(i, 1);
14010 ani_p.hooked = false;
14011 ani_p.playing = false;
14012 ani_p.started = false;
14013 callbacks(ani_p.completes);
14014 }
14015
14016 ranAnis = true;
14017 }
14018
14019 if (!isCore && current.length === 0 && queue.length === 0) {
14020 doneEles.push(ele);
14021 }
14022
14023 return ranAnis;
14024 } // stepElement
14025 // handle all eles
14026
14027
14028 var ranEleAni = false;
14029
14030 for (var e = 0; e < eles.length; e++) {
14031 var ele = eles[e];
14032 var handledThisEle = stepOne(ele);
14033 ranEleAni = ranEleAni || handledThisEle;
14034 } // each element
14035
14036
14037 var ranCoreAni = stepOne(cy, true); // notify renderer
14038
14039 if (ranEleAni || ranCoreAni) {
14040 if (eles.length > 0) {
14041 cy.notify('draw', eles);
14042 } else {
14043 cy.notify('draw');
14044 }
14045 } // remove elements from list of currently animating if its queues are empty
14046
14047
14048 eles.unmerge(doneEles);
14049 cy.emit('step');
14050} // stepAll
14051
14052var corefn$1 = {
14053 // pull in animation functions
14054 animate: define$3.animate(),
14055 animation: define$3.animation(),
14056 animated: define$3.animated(),
14057 clearQueue: define$3.clearQueue(),
14058 delay: define$3.delay(),
14059 delayAnimation: define$3.delayAnimation(),
14060 stop: define$3.stop(),
14061 addToAnimationPool: function addToAnimationPool(eles) {
14062 var cy = this;
14063
14064 if (!cy.styleEnabled()) {
14065 return;
14066 } // save cycles when no style used
14067
14068
14069 cy._private.aniEles.merge(eles);
14070 },
14071 stopAnimationLoop: function stopAnimationLoop() {
14072 this._private.animationsRunning = false;
14073 },
14074 startAnimationLoop: function startAnimationLoop() {
14075 var cy = this;
14076 cy._private.animationsRunning = true;
14077
14078 if (!cy.styleEnabled()) {
14079 return;
14080 } // save cycles when no style used
14081 // NB the animation loop will exec in headless environments if style enabled
14082 // and explicit cy.destroy() is necessary to stop the loop
14083
14084
14085 function headlessStep() {
14086 if (!cy._private.animationsRunning) {
14087 return;
14088 }
14089
14090 requestAnimationFrame(function animationStep(now) {
14091 stepAll(now, cy);
14092 headlessStep();
14093 });
14094 }
14095
14096 var renderer = cy.renderer();
14097
14098 if (renderer && renderer.beforeRender) {
14099 // let the renderer schedule animations
14100 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14101 stepAll(now, cy);
14102 }, renderer.beforeRenderPriorities.animations);
14103 } else {
14104 // manage the animation loop ourselves
14105 headlessStep(); // first call
14106 }
14107 }
14108};
14109
14110var emitterOptions$1 = {
14111 qualifierCompare: function qualifierCompare(selector1, selector2) {
14112 if (selector1 == null || selector2 == null) {
14113 return selector1 == null && selector2 == null;
14114 } else {
14115 return selector1.sameText(selector2);
14116 }
14117 },
14118 eventMatches: function eventMatches(cy, listener, eventObj) {
14119 var selector = listener.qualifier;
14120
14121 if (selector != null) {
14122 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14123 }
14124
14125 return true;
14126 },
14127 addEventFields: function addEventFields(cy, evt) {
14128 evt.cy = cy;
14129 evt.target = cy;
14130 },
14131 callbackContext: function callbackContext(cy, listener, eventObj) {
14132 return listener.qualifier != null ? eventObj.target : cy;
14133 }
14134};
14135
14136var argSelector$1 = function argSelector(arg) {
14137 if (string(arg)) {
14138 return new Selector(arg);
14139 } else {
14140 return arg;
14141 }
14142};
14143
14144var elesfn$v = {
14145 createEmitter: function createEmitter() {
14146 var _p = this._private;
14147
14148 if (!_p.emitter) {
14149 _p.emitter = new Emitter(emitterOptions$1, this);
14150 }
14151
14152 return this;
14153 },
14154 emitter: function emitter() {
14155 return this._private.emitter;
14156 },
14157 on: function on(events, selector, callback) {
14158 this.emitter().on(events, argSelector$1(selector), callback);
14159 return this;
14160 },
14161 removeListener: function removeListener(events, selector, callback) {
14162 this.emitter().removeListener(events, argSelector$1(selector), callback);
14163 return this;
14164 },
14165 removeAllListeners: function removeAllListeners() {
14166 this.emitter().removeAllListeners();
14167 return this;
14168 },
14169 one: function one(events, selector, callback) {
14170 this.emitter().one(events, argSelector$1(selector), callback);
14171 return this;
14172 },
14173 once: function once(events, selector, callback) {
14174 this.emitter().one(events, argSelector$1(selector), callback);
14175 return this;
14176 },
14177 emit: function emit(events, extraParams) {
14178 this.emitter().emit(events, extraParams);
14179 return this;
14180 },
14181 emitAndNotify: function emitAndNotify(event, eles) {
14182 this.emit(event);
14183 this.notify(event, eles);
14184 return this;
14185 }
14186};
14187define$3.eventAliasesOn(elesfn$v);
14188
14189var corefn$2 = {
14190 png: function png(options) {
14191 var renderer = this._private.renderer;
14192 options = options || {};
14193 return renderer.png(options);
14194 },
14195 jpg: function jpg(options) {
14196 var renderer = this._private.renderer;
14197 options = options || {};
14198 options.bg = options.bg || '#fff';
14199 return renderer.jpg(options);
14200 }
14201};
14202corefn$2.jpeg = corefn$2.jpg;
14203
14204var corefn$3 = {
14205 layout: function layout(options) {
14206 var cy = this;
14207
14208 if (options == null) {
14209 error('Layout options must be specified to make a layout');
14210 return;
14211 }
14212
14213 if (options.name == null) {
14214 error('A `name` must be specified to make a layout');
14215 return;
14216 }
14217
14218 var name = options.name;
14219 var Layout = cy.extension('layout', name);
14220
14221 if (Layout == null) {
14222 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14223 return;
14224 }
14225
14226 var eles;
14227
14228 if (string(options.eles)) {
14229 eles = cy.$(options.eles);
14230 } else {
14231 eles = options.eles != null ? options.eles : cy.$();
14232 }
14233
14234 var layout = new Layout(extend({}, options, {
14235 cy: cy,
14236 eles: eles
14237 }));
14238 return layout;
14239 }
14240};
14241corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
14242
14243var corefn$4 = {
14244 notify: function notify(eventName, eventEles) {
14245 var _p = this._private;
14246
14247 if (this.batching()) {
14248 _p.batchNotifications = _p.batchNotifications || {};
14249 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14250
14251 if (eventEles != null) {
14252 eles.merge(eventEles);
14253 }
14254
14255 return; // notifications are disabled during batching
14256 }
14257
14258 if (!_p.notificationsEnabled) {
14259 return;
14260 } // exit on disabled
14261
14262
14263 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
14264
14265 if (this.destroyed() || !renderer) {
14266 return;
14267 }
14268
14269 renderer.notify(eventName, eventEles);
14270 },
14271 notifications: function notifications(bool) {
14272 var p = this._private;
14273
14274 if (bool === undefined) {
14275 return p.notificationsEnabled;
14276 } else {
14277 p.notificationsEnabled = bool ? true : false;
14278 }
14279
14280 return this;
14281 },
14282 noNotifications: function noNotifications(callback) {
14283 this.notifications(false);
14284 callback();
14285 this.notifications(true);
14286 },
14287 batching: function batching() {
14288 return this._private.batchCount > 0;
14289 },
14290 startBatch: function startBatch() {
14291 var _p = this._private;
14292
14293 if (_p.batchCount == null) {
14294 _p.batchCount = 0;
14295 }
14296
14297 if (_p.batchCount === 0) {
14298 _p.batchStyleEles = this.collection();
14299 _p.batchNotifications = {};
14300 }
14301
14302 _p.batchCount++;
14303 return this;
14304 },
14305 endBatch: function endBatch() {
14306 var _p = this._private;
14307
14308 if (_p.batchCount === 0) {
14309 return this;
14310 }
14311
14312 _p.batchCount--;
14313
14314 if (_p.batchCount === 0) {
14315 // update style for dirty eles
14316 _p.batchStyleEles.updateStyle();
14317
14318 var renderer = this.renderer(); // notify the renderer of queued eles and event types
14319
14320 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14321 var eles = _p.batchNotifications[eventName];
14322
14323 if (eles.empty()) {
14324 renderer.notify(eventName);
14325 } else {
14326 renderer.notify(eventName, eles);
14327 }
14328 });
14329 }
14330
14331 return this;
14332 },
14333 batch: function batch(callback) {
14334 this.startBatch();
14335 callback();
14336 this.endBatch();
14337 return this;
14338 },
14339 // for backwards compatibility
14340 batchData: function batchData(map) {
14341 var cy = this;
14342 return this.batch(function () {
14343 var ids = Object.keys(map);
14344
14345 for (var i = 0; i < ids.length; i++) {
14346 var id = ids[i];
14347 var data = map[id];
14348 var ele = cy.getElementById(id);
14349 ele.data(data);
14350 }
14351 });
14352 }
14353};
14354
14355var rendererDefaults = defaults({
14356 hideEdgesOnViewport: false,
14357 textureOnViewport: false,
14358 motionBlur: false,
14359 motionBlurOpacity: 0.05,
14360 pixelRatio: undefined,
14361 desktopTapThreshold: 4,
14362 touchTapThreshold: 8,
14363 wheelSensitivity: 1,
14364 debug: false,
14365 showFps: false
14366});
14367var corefn$5 = {
14368 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14369 var r = this._private.renderer;
14370 r.renderTo(context, zoom, pan, pxRatio);
14371 return this;
14372 },
14373 renderer: function renderer() {
14374 return this._private.renderer;
14375 },
14376 forceRender: function forceRender() {
14377 this.notify('draw');
14378 return this;
14379 },
14380 resize: function resize() {
14381 this.invalidateSize();
14382 this.emitAndNotify('resize');
14383 return this;
14384 },
14385 initRenderer: function initRenderer(options) {
14386 var cy = this;
14387 var RendererProto = cy.extension('renderer', options.name);
14388
14389 if (RendererProto == null) {
14390 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14391 return;
14392 }
14393
14394 if (options.wheelSensitivity !== undefined) {
14395 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.");
14396 }
14397
14398 var rOpts = rendererDefaults(options);
14399 rOpts.cy = cy;
14400 cy._private.renderer = new RendererProto(rOpts);
14401 this.notify('init');
14402 },
14403 destroyRenderer: function destroyRenderer() {
14404 var cy = this;
14405 cy.notify('destroy'); // destroy the renderer
14406
14407 var domEle = cy.container();
14408
14409 if (domEle) {
14410 domEle._cyreg = null;
14411
14412 while (domEle.childNodes.length > 0) {
14413 domEle.removeChild(domEle.childNodes[0]);
14414 }
14415 }
14416
14417 cy._private.renderer = null; // to be extra safe, remove the ref
14418
14419 cy.mutableElements().forEach(function (ele) {
14420 var _p = ele._private;
14421 _p.rscratch = {};
14422 _p.rstyle = {};
14423 _p.animation.current = [];
14424 _p.animation.queue = [];
14425 });
14426 },
14427 onRender: function onRender(fn) {
14428 return this.on('render', fn);
14429 },
14430 offRender: function offRender(fn) {
14431 return this.off('render', fn);
14432 }
14433};
14434corefn$5.invalidateDimensions = corefn$5.resize;
14435
14436var corefn$6 = {
14437 // get a collection
14438 // - empty collection on no args
14439 // - collection of elements in the graph on selector arg
14440 // - guarantee a returned collection when elements or collection specified
14441 collection: function collection(eles, opts) {
14442 if (string(eles)) {
14443 return this.$(eles);
14444 } else if (elementOrCollection(eles)) {
14445 return eles.collection();
14446 } else if (array(eles)) {
14447 return new Collection(this, eles, opts);
14448 }
14449
14450 return new Collection(this);
14451 },
14452 nodes: function nodes(selector) {
14453 var nodes = this.$(function (ele) {
14454 return ele.isNode();
14455 });
14456
14457 if (selector) {
14458 return nodes.filter(selector);
14459 }
14460
14461 return nodes;
14462 },
14463 edges: function edges(selector) {
14464 var edges = this.$(function (ele) {
14465 return ele.isEdge();
14466 });
14467
14468 if (selector) {
14469 return edges.filter(selector);
14470 }
14471
14472 return edges;
14473 },
14474 // search the graph like jQuery
14475 $: function $(selector) {
14476 var eles = this._private.elements;
14477
14478 if (selector) {
14479 return eles.filter(selector);
14480 } else {
14481 return eles.spawnSelf();
14482 }
14483 },
14484 mutableElements: function mutableElements() {
14485 return this._private.elements;
14486 }
14487}; // aliases
14488
14489corefn$6.elements = corefn$6.filter = corefn$6.$;
14490
14491var styfn = {}; // keys for style blocks, e.g. ttfftt
14492
14493var TRUE = 't';
14494var FALSE = 'f'; // (potentially expensive calculation)
14495// apply the style to the element based on
14496// - its bypass
14497// - what selectors match it
14498
14499styfn.apply = function (eles) {
14500 var self = this;
14501 var _p = self._private;
14502 var cy = _p.cy;
14503 var updatedEles = cy.collection();
14504
14505 if (_p.newStyle) {
14506 // clear style caches
14507 _p.contextStyles = {};
14508 _p.propDiffs = {};
14509 self.cleanElements(eles, true);
14510 }
14511
14512 for (var ie = 0; ie < eles.length; ie++) {
14513 var ele = eles[ie];
14514 var cxtMeta = self.getContextMeta(ele);
14515
14516 if (cxtMeta.empty) {
14517 continue;
14518 }
14519
14520 var cxtStyle = self.getContextStyle(cxtMeta);
14521 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
14522
14523 if (!_p.newStyle) {
14524 self.updateTransitions(ele, app.diffProps);
14525 }
14526
14527 var hintsDiff = self.updateStyleHints(ele);
14528
14529 if (hintsDiff) {
14530 updatedEles.push(ele);
14531 }
14532 } // for elements
14533
14534
14535 _p.newStyle = false;
14536 return updatedEles;
14537};
14538
14539styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
14540 var self = this;
14541 var cache = self._private.propDiffs = self._private.propDiffs || {};
14542 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
14543 var cachedVal = cache[dualCxtKey];
14544
14545 if (cachedVal) {
14546 return cachedVal;
14547 }
14548
14549 var diffProps = [];
14550 var addedProp = {};
14551
14552 for (var i = 0; i < self.length; i++) {
14553 var cxt = self[i];
14554 var oldHasCxt = oldCxtKey[i] === TRUE;
14555 var newHasCxt = newCxtKey[i] === TRUE;
14556 var cxtHasDiffed = oldHasCxt !== newHasCxt;
14557 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
14558
14559 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
14560 var props = void 0;
14561
14562 if (cxtHasDiffed && cxtHasMappedProps) {
14563 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
14564 } else if (cxtHasDiffed) {
14565 props = cxt.properties; // need to check them all
14566 } else if (cxtHasMappedProps) {
14567 props = cxt.mappedProperties; // only need to check mapped
14568 }
14569
14570 for (var j = 0; j < props.length; j++) {
14571 var prop = props[j];
14572 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
14573 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
14574 // is cached)
14575
14576 var laterCxtOverrides = false;
14577
14578 for (var k = i + 1; k < self.length; k++) {
14579 var laterCxt = self[k];
14580 var hasLaterCxt = newCxtKey[k] === TRUE;
14581
14582 if (!hasLaterCxt) {
14583 continue;
14584 } // can't override unless the context is active
14585
14586
14587 laterCxtOverrides = laterCxt.properties[prop.name] != null;
14588
14589 if (laterCxtOverrides) {
14590 break;
14591 } // exit early as long as one later context overrides
14592
14593 }
14594
14595 if (!addedProp[name] && !laterCxtOverrides) {
14596 addedProp[name] = true;
14597 diffProps.push(name);
14598 }
14599 } // for props
14600
14601 } // if
14602
14603 } // for contexts
14604
14605
14606 cache[dualCxtKey] = diffProps;
14607 return diffProps;
14608};
14609
14610styfn.getContextMeta = function (ele) {
14611 var self = this;
14612 var cxtKey = '';
14613 var diffProps;
14614 var prevKey = ele._private.styleCxtKey || '';
14615
14616 if (self._private.newStyle) {
14617 prevKey = ''; // since we need to apply all style if a fresh stylesheet
14618 } // get the cxt key
14619
14620
14621 for (var i = 0; i < self.length; i++) {
14622 var context = self[i];
14623 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
14624
14625 if (contextSelectorMatches) {
14626 cxtKey += TRUE;
14627 } else {
14628 cxtKey += FALSE;
14629 }
14630 } // for context
14631
14632
14633 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
14634 ele._private.styleCxtKey = cxtKey;
14635 return {
14636 key: cxtKey,
14637 diffPropNames: diffProps,
14638 empty: diffProps.length === 0
14639 };
14640}; // gets a computed ele style object based on matched contexts
14641
14642
14643styfn.getContextStyle = function (cxtMeta) {
14644 var cxtKey = cxtMeta.key;
14645 var self = this;
14646 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
14647
14648 if (cxtStyles[cxtKey]) {
14649 return cxtStyles[cxtKey];
14650 }
14651
14652 var style = {
14653 _private: {
14654 key: cxtKey
14655 }
14656 };
14657
14658 for (var i = 0; i < self.length; i++) {
14659 var cxt = self[i];
14660 var hasCxt = cxtKey[i] === TRUE;
14661
14662 if (!hasCxt) {
14663 continue;
14664 }
14665
14666 for (var j = 0; j < cxt.properties.length; j++) {
14667 var prop = cxt.properties[j];
14668 style[prop.name] = prop;
14669 }
14670 }
14671
14672 cxtStyles[cxtKey] = style;
14673 return style;
14674};
14675
14676styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
14677 var self = this;
14678 var diffProps = cxtMeta.diffPropNames;
14679 var retDiffProps = {};
14680 var types = self.types;
14681
14682 for (var i = 0; i < diffProps.length; i++) {
14683 var diffPropName = diffProps[i];
14684 var cxtProp = cxtStyle[diffPropName];
14685 var eleProp = ele.pstyle(diffPropName);
14686
14687 if (!cxtProp) {
14688 // no context prop means delete
14689 if (!eleProp) {
14690 continue; // no existing prop means nothing needs to be removed
14691 // nb affects initial application on mapped values like control-point-distances
14692 } else if (eleProp.bypass) {
14693 cxtProp = {
14694 name: diffPropName,
14695 deleteBypassed: true
14696 };
14697 } else {
14698 cxtProp = {
14699 name: diffPropName,
14700 "delete": true
14701 };
14702 }
14703 } // save cycles when the context prop doesn't need to be applied
14704
14705
14706 if (eleProp === cxtProp) {
14707 continue;
14708 } // save cycles when a mapped context prop doesn't need to be applied
14709
14710
14711 if (cxtProp.mapped === types.fn // context prop is function mapper
14712 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
14713 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
14714 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
14715 ) {
14716 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
14717 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
14718
14719 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
14720
14721 if (fnValue === mapping.prevFnValue) {
14722 continue;
14723 }
14724 }
14725
14726 var retDiffProp = retDiffProps[diffPropName] = {
14727 prev: eleProp
14728 };
14729 self.applyParsedProperty(ele, cxtProp);
14730 retDiffProp.next = ele.pstyle(diffPropName);
14731
14732 if (retDiffProp.next && retDiffProp.next.bypass) {
14733 retDiffProp.next = retDiffProp.next.bypassed;
14734 }
14735 }
14736
14737 return {
14738 diffProps: retDiffProps
14739 };
14740};
14741
14742styfn.updateStyleHints = function (ele) {
14743 var _p = ele._private;
14744 var self = this;
14745 var propNames = self.propertyGroupNames;
14746 var propGrKeys = self.propertyGroupKeys;
14747
14748 var propHash = function propHash(ele, propNames, seedKey) {
14749 return self.getPropertiesHash(ele, propNames, seedKey);
14750 };
14751
14752 var oldStyleKey = _p.styleKey;
14753
14754 if (ele.removed()) {
14755 return false;
14756 }
14757
14758 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
14759 // but lazily -- only use non-default prop values to reduce the number of hashes
14760 //
14761
14762 var overriddenStyles = ele._private.style;
14763 propNames = Object.keys(overriddenStyles);
14764
14765 for (var i = 0; i < propGrKeys.length; i++) {
14766 var grKey = propGrKeys[i];
14767 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14768 }
14769
14770 var updateGrKey1 = function updateGrKey1(val, grKey) {
14771 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
14772 };
14773
14774 var updateGrKey2 = function updateGrKey2(val, grKey) {
14775 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
14776 };
14777
14778 var updateGrKey = function updateGrKey(val, grKey) {
14779 updateGrKey1(val, grKey);
14780 updateGrKey2(val, grKey);
14781 };
14782
14783 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
14784 for (var j = 0; j < strVal.length; j++) {
14785 var ch = strVal.charCodeAt(j);
14786 updateGrKey1(ch, grKey);
14787 updateGrKey2(ch, grKey);
14788 }
14789 }; // - hashing works on 32 bit ints b/c we use bitwise ops
14790 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
14791 // - raise up small numbers so more significant digits are seen by hashing
14792 // - make small numbers larger than a normal value to avoid collisions
14793 // - works in practice and it's relatively cheap
14794
14795
14796 var N = 2000000000;
14797
14798 var cleanNum = function cleanNum(val) {
14799 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
14800 };
14801
14802 for (var _i = 0; _i < propNames.length; _i++) {
14803 var name = propNames[_i];
14804 var parsedProp = overriddenStyles[name];
14805
14806 if (parsedProp == null) {
14807 continue;
14808 }
14809
14810 var propInfo = this.properties[name];
14811 var type = propInfo.type;
14812 var _grKey = propInfo.groupKey;
14813 var normalizedNumberVal = void 0;
14814
14815 if (propInfo.hashOverride != null) {
14816 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
14817 } else if (parsedProp.pfValue != null) {
14818 normalizedNumberVal = parsedProp.pfValue;
14819 } // might not be a number if it allows enums
14820
14821
14822 var numberVal = propInfo.enums == null ? parsedProp.value : null;
14823 var haveNormNum = normalizedNumberVal != null;
14824 var haveUnitedNum = numberVal != null;
14825 var haveNum = haveNormNum || haveUnitedNum;
14826 var units = parsedProp.units; // numbers are cheaper to hash than strings
14827 // 1 hash op vs n hash ops (for length n string)
14828
14829 if (type.number && haveNum && !type.multiple) {
14830 var v = haveNormNum ? normalizedNumberVal : numberVal;
14831 updateGrKey(cleanNum(v), _grKey);
14832
14833 if (!haveNormNum && units != null) {
14834 updateGrKeyWStr(units, _grKey);
14835 }
14836 } else {
14837 updateGrKeyWStr(parsedProp.strValue, _grKey);
14838 }
14839 } // overall style key
14840 //
14841
14842
14843 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14844
14845 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
14846 var _grKey2 = propGrKeys[_i2];
14847 var grHash = _p.styleKeys[_grKey2];
14848 hash[0] = hashInt(grHash[0], hash[0]);
14849 hash[1] = hashIntAlt(grHash[1], hash[1]);
14850 }
14851
14852 _p.styleKey = combineHashes(hash[0], hash[1]); // label dims
14853 //
14854
14855 var sk = _p.styleKeys;
14856 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
14857 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
14858 _p.labelKey = combineHashesArray(labelKeys);
14859 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
14860
14861 if (!isNode) {
14862 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
14863 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
14864 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
14865 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
14866 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
14867 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
14868 } // node
14869 //
14870
14871
14872 if (isNode) {
14873 var _p$styleKeys = _p.styleKeys,
14874 nodeBody = _p$styleKeys.nodeBody,
14875 nodeBorder = _p$styleKeys.nodeBorder,
14876 backgroundImage = _p$styleKeys.backgroundImage,
14877 compound = _p$styleKeys.compound,
14878 pie = _p$styleKeys.pie;
14879 var nodeKeys = [nodeBody, nodeBorder, backgroundImage, compound, pie].filter(function (k) {
14880 return k != null;
14881 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
14882 _p.nodeKey = combineHashesArray(nodeKeys);
14883 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
14884 }
14885
14886 return oldStyleKey !== _p.styleKey;
14887};
14888
14889styfn.clearStyleHints = function (ele) {
14890 var _p = ele._private;
14891 _p.styleKeys = {};
14892 _p.styleKey = null;
14893 _p.labelKey = null;
14894 _p.labelStyleKey = null;
14895 _p.sourceLabelKey = null;
14896 _p.sourceLabelStyleKey = null;
14897 _p.targetLabelKey = null;
14898 _p.targetLabelStyleKey = null;
14899 _p.nodeKey = null;
14900 _p.hasPie = null;
14901}; // apply a property to the style (for internal use)
14902// returns whether application was successful
14903//
14904// now, this function flattens the property, and here's how:
14905//
14906// for parsedProp:{ bypass: true, deleteBypass: true }
14907// no property is generated, instead the bypass property in the
14908// element's style is replaced by what's pointed to by the `bypassed`
14909// field in the bypass property (i.e. restoring the property the
14910// bypass was overriding)
14911//
14912// for parsedProp:{ mapped: truthy }
14913// the generated flattenedProp:{ mapping: prop }
14914//
14915// for parsedProp:{ bypass: true }
14916// the generated flattenedProp:{ bypassed: parsedProp }
14917
14918
14919styfn.applyParsedProperty = function (ele, parsedProp) {
14920 var self = this;
14921 var prop = parsedProp;
14922 var style = ele._private.style;
14923 var flatProp;
14924 var types = self.types;
14925 var type = self.properties[prop.name].type;
14926 var propIsBypass = prop.bypass;
14927 var origProp = style[prop.name];
14928 var origPropIsBypass = origProp && origProp.bypass;
14929 var _p = ele._private;
14930 var flatPropMapping = 'mapping';
14931
14932 var getVal = function getVal(p) {
14933 if (p == null) {
14934 return null;
14935 } else if (p.pfValue != null) {
14936 return p.pfValue;
14937 } else {
14938 return p.value;
14939 }
14940 };
14941
14942 var checkTriggers = function checkTriggers() {
14943 var fromVal = getVal(origProp);
14944 var toVal = getVal(prop);
14945 self.checkTriggers(ele, prop.name, fromVal, toVal);
14946 }; // edge sanity checks to prevent the client from making serious mistakes
14947
14948
14949 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
14950 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
14951 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
14952 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
14953 }
14954
14955 if (prop["delete"]) {
14956 // delete the property and use the default value on falsey value
14957 style[prop.name] = undefined;
14958 checkTriggers();
14959 return true;
14960 }
14961
14962 if (prop.deleteBypassed) {
14963 // delete the property that the
14964 if (!origProp) {
14965 checkTriggers();
14966 return true; // can't delete if no prop
14967 } else if (origProp.bypass) {
14968 // delete bypassed
14969 origProp.bypassed = undefined;
14970 checkTriggers();
14971 return true;
14972 } else {
14973 return false; // we're unsuccessful deleting the bypassed
14974 }
14975 } // check if we need to delete the current bypass
14976
14977
14978 if (prop.deleteBypass) {
14979 // then this property is just here to indicate we need to delete
14980 if (!origProp) {
14981 checkTriggers();
14982 return true; // property is already not defined
14983 } else if (origProp.bypass) {
14984 // then replace the bypass property with the original
14985 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
14986 style[prop.name] = origProp.bypassed;
14987 checkTriggers();
14988 return true;
14989 } else {
14990 return false; // we're unsuccessful deleting the bypass
14991 }
14992 }
14993
14994 var printMappingErr = function printMappingErr() {
14995 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');
14996 }; // put the property in the style objects
14997
14998
14999 switch (prop.mapped) {
15000 // flatten the property if mapped
15001 case types.mapData:
15002 {
15003 // flatten the field (e.g. data.foo.bar)
15004 var fields = prop.field.split('.');
15005 var fieldVal = _p.data;
15006
15007 for (var i = 0; i < fields.length && fieldVal; i++) {
15008 var field = fields[i];
15009 fieldVal = fieldVal[field];
15010 }
15011
15012 if (fieldVal == null) {
15013 printMappingErr();
15014 return false;
15015 }
15016
15017 var percent;
15018
15019 if (!number(fieldVal)) {
15020 // then don't apply and fall back on the existing style
15021 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15022 return false;
15023 } else {
15024 var fieldWidth = prop.fieldMax - prop.fieldMin;
15025
15026 if (fieldWidth === 0) {
15027 // safety check -- not strictly necessary as no props of zero range should be passed here
15028 percent = 0;
15029 } else {
15030 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15031 }
15032 } // make sure to bound percent value
15033
15034
15035 if (percent < 0) {
15036 percent = 0;
15037 } else if (percent > 1) {
15038 percent = 1;
15039 }
15040
15041 if (type.color) {
15042 var r1 = prop.valueMin[0];
15043 var r2 = prop.valueMax[0];
15044 var g1 = prop.valueMin[1];
15045 var g2 = prop.valueMax[1];
15046 var b1 = prop.valueMin[2];
15047 var b2 = prop.valueMax[2];
15048 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15049 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15050 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)];
15051 flatProp = {
15052 // colours are simple, so just create the flat property instead of expensive string parsing
15053 bypass: prop.bypass,
15054 // we're a bypass if the mapping property is a bypass
15055 name: prop.name,
15056 value: clr,
15057 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15058 };
15059 } else if (type.number) {
15060 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15061 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15062 } else {
15063 return false; // can only map to colours and numbers
15064 }
15065
15066 if (!flatProp) {
15067 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15068 printMappingErr();
15069 return false;
15070 }
15071
15072 flatProp.mapping = prop; // keep a reference to the mapping
15073
15074 prop = flatProp; // the flattened (mapped) property is the one we want
15075
15076 break;
15077 }
15078 // direct mapping
15079
15080 case types.data:
15081 {
15082 // flatten the field (e.g. data.foo.bar)
15083 var _fields = prop.field.split('.');
15084
15085 var _fieldVal = _p.data;
15086
15087 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15088 var _field = _fields[_i3];
15089 _fieldVal = _fieldVal[_field];
15090 }
15091
15092 if (_fieldVal != null) {
15093 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15094 }
15095
15096 if (!flatProp) {
15097 // if we can't flatten the property, then don't apply and fall back on the existing style
15098 printMappingErr();
15099 return false;
15100 }
15101
15102 flatProp.mapping = prop; // keep a reference to the mapping
15103
15104 prop = flatProp; // the flattened (mapped) property is the one we want
15105
15106 break;
15107 }
15108
15109 case types.fn:
15110 {
15111 var fn = prop.value;
15112 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15113
15114 prop.prevFnValue = fnRetVal;
15115
15116 if (fnRetVal == null) {
15117 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15118 return false;
15119 }
15120
15121 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15122
15123 if (!flatProp) {
15124 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15125 return false;
15126 }
15127
15128 flatProp.mapping = copy(prop); // keep a reference to the mapping
15129
15130 prop = flatProp; // the flattened (mapped) property is the one we want
15131
15132 break;
15133 }
15134
15135 case undefined:
15136 break;
15137 // just set the property
15138
15139 default:
15140 return false;
15141 // not a valid mapping
15142 } // if the property is a bypass property, then link the resultant property to the original one
15143
15144
15145 if (propIsBypass) {
15146 if (origPropIsBypass) {
15147 // then this bypass overrides the existing one
15148 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15149 } else {
15150 // then link the orig prop to the new bypass
15151 prop.bypassed = origProp;
15152 }
15153
15154 style[prop.name] = prop; // and set
15155 } else {
15156 // prop is not bypass
15157 if (origPropIsBypass) {
15158 // then keep the orig prop (since it's a bypass) and link to the new prop
15159 origProp.bypassed = prop;
15160 } else {
15161 // then just replace the old prop with the new one
15162 style[prop.name] = prop;
15163 }
15164 }
15165
15166 checkTriggers();
15167 return true;
15168};
15169
15170styfn.cleanElements = function (eles, keepBypasses) {
15171 for (var i = 0; i < eles.length; i++) {
15172 var ele = eles[i];
15173 this.clearStyleHints(ele);
15174 ele.dirtyCompoundBoundsCache();
15175 ele.dirtyBoundingBoxCache();
15176
15177 if (!keepBypasses) {
15178 ele._private.style = {};
15179 } else {
15180 var style = ele._private.style;
15181 var propNames = Object.keys(style);
15182
15183 for (var j = 0; j < propNames.length; j++) {
15184 var propName = propNames[j];
15185 var eleProp = style[propName];
15186
15187 if (eleProp != null) {
15188 if (eleProp.bypass) {
15189 eleProp.bypassed = null;
15190 } else {
15191 style[propName] = null;
15192 }
15193 }
15194 }
15195 }
15196 }
15197}; // updates the visual style for all elements (useful for manual style modification after init)
15198
15199
15200styfn.update = function () {
15201 var cy = this._private.cy;
15202 var eles = cy.mutableElements();
15203 eles.updateStyle();
15204}; // diffProps : { name => { prev, next } }
15205
15206
15207styfn.updateTransitions = function (ele, diffProps) {
15208 var self = this;
15209 var _p = ele._private;
15210 var props = ele.pstyle('transition-property').value;
15211 var duration = ele.pstyle('transition-duration').pfValue;
15212 var delay = ele.pstyle('transition-delay').pfValue;
15213
15214 if (props.length > 0 && duration > 0) {
15215 var style = {}; // build up the style to animate towards
15216
15217 var anyPrev = false;
15218
15219 for (var i = 0; i < props.length; i++) {
15220 var prop = props[i];
15221 var styProp = ele.pstyle(prop);
15222 var diffProp = diffProps[prop];
15223
15224 if (!diffProp) {
15225 continue;
15226 }
15227
15228 var prevProp = diffProp.prev;
15229 var fromProp = prevProp;
15230 var toProp = diffProp.next != null ? diffProp.next : styProp;
15231 var diff = false;
15232 var initVal = void 0;
15233 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15234
15235 if (!fromProp) {
15236 continue;
15237 } // consider px values
15238
15239
15240 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
15241 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15242
15243 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
15244 } else if (number(fromProp.value) && number(toProp.value)) {
15245 diff = toProp.value - fromProp.value; // nonzero is truthy
15246
15247 initVal = fromProp.value + initDt * diff; // consider colour values
15248 } else if (array(fromProp.value) && array(toProp.value)) {
15249 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15250 initVal = fromProp.strValue;
15251 } // the previous value is good for an animation only if it's different
15252
15253
15254 if (diff) {
15255 style[prop] = toProp.strValue; // to val
15256
15257 this.applyBypass(ele, prop, initVal); // from val
15258
15259 anyPrev = true;
15260 }
15261 } // end if props allow ani
15262 // can't transition if there's nothing previous to transition from
15263
15264
15265 if (!anyPrev) {
15266 return;
15267 }
15268
15269 _p.transitioning = true;
15270 new Promise$1(function (resolve) {
15271 if (delay > 0) {
15272 ele.delayAnimation(delay).play().promise().then(resolve);
15273 } else {
15274 resolve();
15275 }
15276 }).then(function () {
15277 return ele.animation({
15278 style: style,
15279 duration: duration,
15280 easing: ele.pstyle('transition-timing-function').value,
15281 queue: false
15282 }).play().promise();
15283 }).then(function () {
15284 // if( !isBypass ){
15285 self.removeBypasses(ele, props);
15286 ele.emitAndNotify('style'); // }
15287
15288 _p.transitioning = false;
15289 });
15290 } else if (_p.transitioning) {
15291 this.removeBypasses(ele, props);
15292 ele.emitAndNotify('style');
15293 _p.transitioning = false;
15294 }
15295};
15296
15297styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15298 var prop = this.properties[name];
15299 var triggerCheck = getTrigger(prop);
15300
15301 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15302 onTrigger(prop);
15303 }
15304};
15305
15306styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15307 var _this = this;
15308
15309 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15310 return prop.triggersZOrder;
15311 }, function () {
15312 _this._private.cy.notify('zorder', ele);
15313 });
15314};
15315
15316styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15317 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15318 return prop.triggersBounds;
15319 }, function (prop) {
15320 ele.dirtyCompoundBoundsCache();
15321 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
15322 // then dirty the pll edge bb cache as well
15323
15324 if ( // only for beziers -- so performance of other edges isn't affected
15325 name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') && prop.triggersBoundsOfParallelBeziers) {
15326 ele.parallelEdges().forEach(function (pllEdge) {
15327 if (pllEdge.isBundledBezier()) {
15328 pllEdge.dirtyBoundingBoxCache();
15329 }
15330 });
15331 }
15332 });
15333};
15334
15335styfn.checkTriggers = function (ele, name, fromValue, toValue) {
15336 ele.dirtyStyleCache();
15337 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15338 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15339};
15340
15341var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
15342// returns true iff application was successful for at least 1 specified property
15343
15344styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
15345 var self = this;
15346 var props = [];
15347 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
15348
15349 if (name === '*' || name === '**') {
15350 // apply to all property names
15351 if (value !== undefined) {
15352 for (var i = 0; i < self.properties.length; i++) {
15353 var prop = self.properties[i];
15354 var _name = prop.name;
15355 var parsedProp = this.parse(_name, value, true);
15356
15357 if (parsedProp) {
15358 props.push(parsedProp);
15359 }
15360 }
15361 }
15362 } else if (string(name)) {
15363 // then parse the single property
15364 var _parsedProp = this.parse(name, value, true);
15365
15366 if (_parsedProp) {
15367 props.push(_parsedProp);
15368 }
15369 } else if (plainObject(name)) {
15370 // then parse each property
15371 var specifiedProps = name;
15372 updateTransitions = value;
15373 var names = Object.keys(specifiedProps);
15374
15375 for (var _i = 0; _i < names.length; _i++) {
15376 var _name2 = names[_i];
15377 var _value = specifiedProps[_name2];
15378
15379 if (_value === undefined) {
15380 // try camel case name too
15381 _value = specifiedProps[dash2camel(_name2)];
15382 }
15383
15384 if (_value !== undefined) {
15385 var _parsedProp2 = this.parse(_name2, _value, true);
15386
15387 if (_parsedProp2) {
15388 props.push(_parsedProp2);
15389 }
15390 }
15391 }
15392 } else {
15393 // can't do anything without well defined properties
15394 return false;
15395 } // we've failed if there are no valid properties
15396
15397
15398 if (props.length === 0) {
15399 return false;
15400 } // now, apply the bypass properties on the elements
15401
15402
15403 var ret = false; // return true if at least one succesful bypass applied
15404
15405 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15406 // for each ele
15407 var ele = eles[_i2];
15408 var diffProps = {};
15409 var diffProp = void 0;
15410
15411 for (var j = 0; j < props.length; j++) {
15412 // for each prop
15413 var _prop = props[j];
15414
15415 if (updateTransitions) {
15416 var prevProp = ele.pstyle(_prop.name);
15417 diffProp = diffProps[_prop.name] = {
15418 prev: prevProp
15419 };
15420 }
15421
15422 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
15423
15424 if (updateTransitions) {
15425 diffProp.next = ele.pstyle(_prop.name);
15426 }
15427 } // for props
15428
15429
15430 if (ret) {
15431 this.updateStyleHints(ele);
15432 }
15433
15434 if (updateTransitions) {
15435 this.updateTransitions(ele, diffProps, isBypass);
15436 }
15437 } // for eles
15438
15439
15440 return ret;
15441}; // only useful in specific cases like animation
15442
15443
15444styfn$1.overrideBypass = function (eles, name, value) {
15445 name = camel2dash(name);
15446
15447 for (var i = 0; i < eles.length; i++) {
15448 var ele = eles[i];
15449 var prop = ele._private.style[name];
15450 var type = this.properties[name].type;
15451 var isColor = type.color;
15452 var isMulti = type.mutiple;
15453 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15454
15455 if (!prop || !prop.bypass) {
15456 // need a bypass if one doesn't exist
15457 this.applyBypass(ele, name, value);
15458 } else {
15459 prop.value = value;
15460
15461 if (prop.pfValue != null) {
15462 prop.pfValue = value;
15463 }
15464
15465 if (isColor) {
15466 prop.strValue = 'rgb(' + value.join(',') + ')';
15467 } else if (isMulti) {
15468 prop.strValue = value.join(' ');
15469 } else {
15470 prop.strValue = '' + value;
15471 }
15472
15473 this.updateStyleHints(ele);
15474 }
15475
15476 this.checkTriggers(ele, name, oldValue, value);
15477 }
15478};
15479
15480styfn$1.removeAllBypasses = function (eles, updateTransitions) {
15481 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15482};
15483
15484styfn$1.removeBypasses = function (eles, props, updateTransitions) {
15485 var isBypass = true;
15486
15487 for (var j = 0; j < eles.length; j++) {
15488 var ele = eles[j];
15489 var diffProps = {};
15490
15491 for (var i = 0; i < props.length; i++) {
15492 var name = props[i];
15493 var prop = this.properties[name];
15494 var prevProp = ele.pstyle(prop.name);
15495
15496 if (!prevProp || !prevProp.bypass) {
15497 // if a bypass doesn't exist for the prop, nothing needs to be removed
15498 continue;
15499 }
15500
15501 var value = ''; // empty => remove bypass
15502
15503 var parsedProp = this.parse(name, value, true);
15504 var diffProp = diffProps[prop.name] = {
15505 prev: prevProp
15506 };
15507 this.applyParsedProperty(ele, parsedProp);
15508 diffProp.next = ele.pstyle(prop.name);
15509 } // for props
15510
15511
15512 this.updateStyleHints(ele);
15513
15514 if (updateTransitions) {
15515 this.updateTransitions(ele, diffProps, isBypass);
15516 }
15517 } // for eles
15518
15519};
15520
15521var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
15522
15523styfn$2.getEmSizeInPixels = function () {
15524 var px = this.containerCss('font-size');
15525
15526 if (px != null) {
15527 return parseFloat(px);
15528 } else {
15529 return 1; // for headless
15530 }
15531}; // gets css property from the core container
15532
15533
15534styfn$2.containerCss = function (propName) {
15535 var cy = this._private.cy;
15536 var domElement = cy.container();
15537
15538 if (window$1 && domElement && window$1.getComputedStyle) {
15539 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
15540 }
15541};
15542
15543var styfn$3 = {}; // gets the rendered style for an element
15544
15545styfn$3.getRenderedStyle = function (ele, prop) {
15546 if (prop) {
15547 return this.getStylePropertyValue(ele, prop, true);
15548 } else {
15549 return this.getRawStyle(ele, true);
15550 }
15551}; // gets the raw style for an element
15552
15553
15554styfn$3.getRawStyle = function (ele, isRenderedVal) {
15555 var self = this;
15556 ele = ele[0]; // insure it's an element
15557
15558 if (ele) {
15559 var rstyle = {};
15560
15561 for (var i = 0; i < self.properties.length; i++) {
15562 var prop = self.properties[i];
15563 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15564
15565 if (val != null) {
15566 rstyle[prop.name] = val;
15567 rstyle[dash2camel(prop.name)] = val;
15568 }
15569 }
15570
15571 return rstyle;
15572 }
15573};
15574
15575styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
15576 var pstyle = ele.pstyle(property)[subproperty][index];
15577 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15578};
15579
15580styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15581 var self = this;
15582 ele = ele[0]; // insure it's an element
15583
15584 if (ele) {
15585 var prop = self.properties[propName];
15586
15587 if (prop.alias) {
15588 prop = prop.pointsTo;
15589 }
15590
15591 var type = prop.type;
15592 var styleProp = ele.pstyle(prop.name);
15593
15594 if (styleProp) {
15595 var value = styleProp.value,
15596 units = styleProp.units,
15597 strValue = styleProp.strValue;
15598
15599 if (isRenderedVal && type.number && value != null && number(value)) {
15600 var zoom = ele.cy().zoom();
15601
15602 var getRenderedValue = function getRenderedValue(val) {
15603 return val * zoom;
15604 };
15605
15606 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15607 return getRenderedValue(val) + units;
15608 };
15609
15610 var isArrayValue = array(value);
15611 var haveUnits = isArrayValue ? units.every(function (u) {
15612 return u != null;
15613 }) : units != null;
15614
15615 if (haveUnits) {
15616 if (isArrayValue) {
15617 return value.map(function (v, i) {
15618 return getValueStringWithUnits(v, units[i]);
15619 }).join(' ');
15620 } else {
15621 return getValueStringWithUnits(value, units);
15622 }
15623 } else {
15624 if (isArrayValue) {
15625 return value.map(function (v) {
15626 return string(v) ? v : '' + getRenderedValue(v);
15627 }).join(' ');
15628 } else {
15629 return '' + getRenderedValue(value);
15630 }
15631 }
15632 } else if (strValue != null) {
15633 return strValue;
15634 }
15635 }
15636
15637 return null;
15638 }
15639};
15640
15641styfn$3.getAnimationStartStyle = function (ele, aniProps) {
15642 var rstyle = {};
15643
15644 for (var i = 0; i < aniProps.length; i++) {
15645 var aniProp = aniProps[i];
15646 var name = aniProp.name;
15647 var styleProp = ele.pstyle(name);
15648
15649 if (styleProp !== undefined) {
15650 // then make a prop of it
15651 if (plainObject(styleProp)) {
15652 styleProp = this.parse(name, styleProp.strValue);
15653 } else {
15654 styleProp = this.parse(name, styleProp);
15655 }
15656 }
15657
15658 if (styleProp) {
15659 rstyle[name] = styleProp;
15660 }
15661 }
15662
15663 return rstyle;
15664};
15665
15666styfn$3.getPropsList = function (propsObj) {
15667 var self = this;
15668 var rstyle = [];
15669 var style = propsObj;
15670 var props = self.properties;
15671
15672 if (style) {
15673 var names = Object.keys(style);
15674
15675 for (var i = 0; i < names.length; i++) {
15676 var name = names[i];
15677 var val = style[name];
15678 var prop = props[name] || props[camel2dash(name)];
15679 var styleProp = this.parse(prop.name, val);
15680
15681 if (styleProp) {
15682 rstyle.push(styleProp);
15683 }
15684 }
15685 }
15686
15687 return rstyle;
15688};
15689
15690styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
15691 var hash = seed.slice();
15692 var name, val, strVal, chVal;
15693 var i, j;
15694
15695 for (i = 0; i < propNames.length; i++) {
15696 name = propNames[i];
15697 val = ele.pstyle(name, false);
15698
15699 if (val == null) {
15700 continue;
15701 } else if (val.pfValue != null) {
15702 hash[0] = hashInt(chVal, hash[0]);
15703 hash[1] = hashIntAlt(chVal, hash[1]);
15704 } else {
15705 strVal = val.strValue;
15706
15707 for (j = 0; j < strVal.length; j++) {
15708 chVal = strVal.charCodeAt(j);
15709 hash[0] = hashInt(chVal, hash[0]);
15710 hash[1] = hashIntAlt(chVal, hash[1]);
15711 }
15712 }
15713 }
15714
15715 return hash;
15716};
15717
15718styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
15719
15720var styfn$4 = {};
15721
15722styfn$4.appendFromJson = function (json) {
15723 var style = this;
15724
15725 for (var i = 0; i < json.length; i++) {
15726 var context = json[i];
15727 var selector = context.selector;
15728 var props = context.style || context.css;
15729 var names = Object.keys(props);
15730 style.selector(selector); // apply selector
15731
15732 for (var j = 0; j < names.length; j++) {
15733 var name = names[j];
15734 var value = props[name];
15735 style.css(name, value); // apply property
15736 }
15737 }
15738
15739 return style;
15740}; // accessible cy.style() function
15741
15742
15743styfn$4.fromJson = function (json) {
15744 var style = this;
15745 style.resetToDefault();
15746 style.appendFromJson(json);
15747 return style;
15748}; // get json from cy.style() api
15749
15750
15751styfn$4.json = function () {
15752 var json = [];
15753
15754 for (var i = this.defaultLength; i < this.length; i++) {
15755 var cxt = this[i];
15756 var selector = cxt.selector;
15757 var props = cxt.properties;
15758 var css = {};
15759
15760 for (var j = 0; j < props.length; j++) {
15761 var prop = props[j];
15762 css[prop.name] = prop.strValue;
15763 }
15764
15765 json.push({
15766 selector: !selector ? 'core' : selector.toString(),
15767 style: css
15768 });
15769 }
15770
15771 return json;
15772};
15773
15774var styfn$5 = {};
15775
15776styfn$5.appendFromString = function (string) {
15777 var self = this;
15778 var style = this;
15779 var remaining = '' + string;
15780 var selAndBlockStr;
15781 var blockRem;
15782 var propAndValStr; // remove comments from the style string
15783
15784 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
15785
15786 function removeSelAndBlockFromRemaining() {
15787 // remove the parsed selector and block from the remaining text to parse
15788 if (remaining.length > selAndBlockStr.length) {
15789 remaining = remaining.substr(selAndBlockStr.length);
15790 } else {
15791 remaining = '';
15792 }
15793 }
15794
15795 function removePropAndValFromRem() {
15796 // remove the parsed property and value from the remaining block text to parse
15797 if (blockRem.length > propAndValStr.length) {
15798 blockRem = blockRem.substr(propAndValStr.length);
15799 } else {
15800 blockRem = '';
15801 }
15802 }
15803
15804 for (;;) {
15805 var nothingLeftToParse = remaining.match(/^\s*$/);
15806
15807 if (nothingLeftToParse) {
15808 break;
15809 }
15810
15811 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
15812
15813 if (!selAndBlock) {
15814 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
15815 break;
15816 }
15817
15818 selAndBlockStr = selAndBlock[0]; // parse the selector
15819
15820 var selectorStr = selAndBlock[1];
15821
15822 if (selectorStr !== 'core') {
15823 var selector = new Selector(selectorStr);
15824
15825 if (selector.invalid) {
15826 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
15827
15828 removeSelAndBlockFromRemaining();
15829 continue;
15830 }
15831 } // parse the block of properties and values
15832
15833
15834 var blockStr = selAndBlock[2];
15835 var invalidBlock = false;
15836 blockRem = blockStr;
15837 var props = [];
15838
15839 for (;;) {
15840 var _nothingLeftToParse = blockRem.match(/^\s*$/);
15841
15842 if (_nothingLeftToParse) {
15843 break;
15844 }
15845
15846 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
15847
15848 if (!propAndVal) {
15849 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
15850 invalidBlock = true;
15851 break;
15852 }
15853
15854 propAndValStr = propAndVal[0];
15855 var propStr = propAndVal[1];
15856 var valStr = propAndVal[2];
15857 var prop = self.properties[propStr];
15858
15859 if (!prop) {
15860 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
15861
15862 removePropAndValFromRem();
15863 continue;
15864 }
15865
15866 var parsedProp = style.parse(propStr, valStr);
15867
15868 if (!parsedProp) {
15869 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
15870
15871 removePropAndValFromRem();
15872 continue;
15873 }
15874
15875 props.push({
15876 name: propStr,
15877 val: valStr
15878 });
15879 removePropAndValFromRem();
15880 }
15881
15882 if (invalidBlock) {
15883 removeSelAndBlockFromRemaining();
15884 break;
15885 } // put the parsed block in the style
15886
15887
15888 style.selector(selectorStr);
15889
15890 for (var i = 0; i < props.length; i++) {
15891 var _prop = props[i];
15892 style.css(_prop.name, _prop.val);
15893 }
15894
15895 removeSelAndBlockFromRemaining();
15896 }
15897
15898 return style;
15899};
15900
15901styfn$5.fromString = function (string) {
15902 var style = this;
15903 style.resetToDefault();
15904 style.appendFromString(string);
15905 return style;
15906};
15907
15908var styfn$6 = {};
15909
15910(function () {
15911 var number = number$1;
15912 var rgba = rgbaNoBackRefs;
15913 var hsla = hslaNoBackRefs;
15914 var hex3$1 = hex3;
15915 var hex6$1 = hex6;
15916
15917 var data = function data(prefix) {
15918 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
15919 };
15920
15921 var mapData = function mapData(prefix) {
15922 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
15923 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
15924 };
15925
15926 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
15927
15928 styfn$6.types = {
15929 time: {
15930 number: true,
15931 min: 0,
15932 units: 's|ms',
15933 implicitUnits: 'ms'
15934 },
15935 percent: {
15936 number: true,
15937 min: 0,
15938 max: 100,
15939 units: '%',
15940 implicitUnits: '%'
15941 },
15942 percentages: {
15943 number: true,
15944 min: 0,
15945 max: 100,
15946 units: '%',
15947 implicitUnits: '%',
15948 multiple: true
15949 },
15950 zeroOneNumber: {
15951 number: true,
15952 min: 0,
15953 max: 1,
15954 unitless: true
15955 },
15956 zeroOneNumbers: {
15957 number: true,
15958 min: 0,
15959 max: 1,
15960 unitless: true,
15961 multiple: true
15962 },
15963 nOneOneNumber: {
15964 number: true,
15965 min: -1,
15966 max: 1,
15967 unitless: true
15968 },
15969 nonNegativeInt: {
15970 number: true,
15971 min: 0,
15972 integer: true,
15973 unitless: true
15974 },
15975 position: {
15976 enums: ['parent', 'origin']
15977 },
15978 nodeSize: {
15979 number: true,
15980 min: 0,
15981 enums: ['label']
15982 },
15983 number: {
15984 number: true,
15985 unitless: true
15986 },
15987 numbers: {
15988 number: true,
15989 unitless: true,
15990 multiple: true
15991 },
15992 positiveNumber: {
15993 number: true,
15994 unitless: true,
15995 min: 0,
15996 strictMin: true
15997 },
15998 size: {
15999 number: true,
16000 min: 0
16001 },
16002 bidirectionalSize: {
16003 number: true
16004 },
16005 // allows negative
16006 bidirectionalSizeMaybePercent: {
16007 number: true,
16008 allowPercent: true
16009 },
16010 // allows negative
16011 bidirectionalSizes: {
16012 number: true,
16013 multiple: true
16014 },
16015 // allows negative
16016 sizeMaybePercent: {
16017 number: true,
16018 min: 0,
16019 allowPercent: true
16020 },
16021 axisDirection: {
16022 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16023 },
16024 paddingRelativeTo: {
16025 enums: ['width', 'height', 'average', 'min', 'max']
16026 },
16027 bgWH: {
16028 number: true,
16029 min: 0,
16030 allowPercent: true,
16031 enums: ['auto'],
16032 multiple: true
16033 },
16034 bgPos: {
16035 number: true,
16036 allowPercent: true,
16037 multiple: true
16038 },
16039 bgRelativeTo: {
16040 enums: ['inner', 'include-padding'],
16041 multiple: true
16042 },
16043 bgRepeat: {
16044 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16045 multiple: true
16046 },
16047 bgFit: {
16048 enums: ['none', 'contain', 'cover'],
16049 multiple: true
16050 },
16051 bgCrossOrigin: {
16052 enums: ['anonymous', 'use-credentials'],
16053 multiple: true
16054 },
16055 bgClip: {
16056 enums: ['none', 'node'],
16057 multiple: true
16058 },
16059 color: {
16060 color: true
16061 },
16062 colors: {
16063 color: true,
16064 multiple: true
16065 },
16066 fill: {
16067 enums: ['solid', 'linear-gradient', 'radial-gradient']
16068 },
16069 bool: {
16070 enums: ['yes', 'no']
16071 },
16072 lineStyle: {
16073 enums: ['solid', 'dotted', 'dashed']
16074 },
16075 lineCap: {
16076 enums: ['butt', 'round', 'square']
16077 },
16078 borderStyle: {
16079 enums: ['solid', 'dotted', 'dashed', 'double']
16080 },
16081 curveStyle: {
16082 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
16083 },
16084 fontFamily: {
16085 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16086 },
16087 fontStyle: {
16088 enums: ['italic', 'normal', 'oblique']
16089 },
16090 fontWeight: {
16091 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16092 },
16093 textDecoration: {
16094 enums: ['none', 'underline', 'overline', 'line-through']
16095 },
16096 textTransform: {
16097 enums: ['none', 'uppercase', 'lowercase']
16098 },
16099 textWrap: {
16100 enums: ['none', 'wrap', 'ellipsis']
16101 },
16102 textOverflowWrap: {
16103 enums: ['whitespace', 'anywhere']
16104 },
16105 textBackgroundShape: {
16106 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16107 },
16108 nodeShape: {
16109 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']
16110 },
16111 compoundIncludeLabels: {
16112 enums: ['include', 'exclude']
16113 },
16114 arrowShape: {
16115 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16116 },
16117 arrowFill: {
16118 enums: ['filled', 'hollow']
16119 },
16120 display: {
16121 enums: ['element', 'none']
16122 },
16123 visibility: {
16124 enums: ['hidden', 'visible']
16125 },
16126 zCompoundDepth: {
16127 enums: ['bottom', 'orphan', 'auto', 'top']
16128 },
16129 zIndexCompare: {
16130 enums: ['auto', 'manual']
16131 },
16132 valign: {
16133 enums: ['top', 'center', 'bottom']
16134 },
16135 halign: {
16136 enums: ['left', 'center', 'right']
16137 },
16138 justification: {
16139 enums: ['left', 'center', 'right', 'auto']
16140 },
16141 text: {
16142 string: true
16143 },
16144 data: {
16145 mapping: true,
16146 regex: data('data')
16147 },
16148 layoutData: {
16149 mapping: true,
16150 regex: data('layoutData')
16151 },
16152 scratch: {
16153 mapping: true,
16154 regex: data('scratch')
16155 },
16156 mapData: {
16157 mapping: true,
16158 regex: mapData('mapData')
16159 },
16160 mapLayoutData: {
16161 mapping: true,
16162 regex: mapData('mapLayoutData')
16163 },
16164 mapScratch: {
16165 mapping: true,
16166 regex: mapData('mapScratch')
16167 },
16168 fn: {
16169 mapping: true,
16170 fn: true
16171 },
16172 url: {
16173 regexes: urlRegexes,
16174 singleRegexMatchValue: true
16175 },
16176 urls: {
16177 regexes: urlRegexes,
16178 singleRegexMatchValue: true,
16179 multiple: true
16180 },
16181 propList: {
16182 propList: true
16183 },
16184 angle: {
16185 number: true,
16186 units: 'deg|rad',
16187 implicitUnits: 'rad'
16188 },
16189 textRotation: {
16190 number: true,
16191 units: 'deg|rad',
16192 implicitUnits: 'rad',
16193 enums: ['none', 'autorotate']
16194 },
16195 polygonPointList: {
16196 number: true,
16197 multiple: true,
16198 evenMultiple: true,
16199 min: -1,
16200 max: 1,
16201 unitless: true
16202 },
16203 edgeDistances: {
16204 enums: ['intersection', 'node-position']
16205 },
16206 edgeEndpoint: {
16207 number: true,
16208 multiple: true,
16209 units: '%|px|em|deg|rad',
16210 implicitUnits: 'px',
16211 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16212 singleEnum: true,
16213 validate: function validate(valArr, unitsArr) {
16214 switch (valArr.length) {
16215 case 2:
16216 // can be % or px only
16217 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16218
16219 case 1:
16220 // can be enum, deg, or rad only
16221 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16222
16223 default:
16224 return false;
16225 }
16226 }
16227 },
16228 easing: {
16229 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
16230 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']
16231 },
16232 gradientDirection: {
16233 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']
16234 },
16235 boundsExpansion: {
16236 number: true,
16237 multiple: true,
16238 min: 0,
16239 validate: function validate(valArr) {
16240 var length = valArr.length;
16241 return length === 1 || length === 2 || length === 4;
16242 }
16243 }
16244 };
16245 var diff = {
16246 zeroNonZero: function zeroNonZero(val1, val2) {
16247 if ((val1 == null || val2 == null) && val1 !== val2) {
16248 return true; // null cases could represent any value
16249 }
16250
16251 if (val1 == 0 && val2 != 0) {
16252 return true;
16253 } else if (val1 != 0 && val2 == 0) {
16254 return true;
16255 } else {
16256 return false;
16257 }
16258 },
16259 any: function any(val1, val2) {
16260 return val1 != val2;
16261 },
16262 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
16263 var empty1 = emptyString(str1);
16264 var empty2 = emptyString(str2);
16265 return empty1 && !empty2 || !empty1 && empty2;
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 triggersZOrder: diff.emptyNonEmpty
16278 }, {
16279 name: 'text-rotation',
16280 type: t.textRotation,
16281 triggersBounds: diff.any
16282 }, {
16283 name: 'text-margin-x',
16284 type: t.bidirectionalSize,
16285 triggersBounds: diff.any
16286 }, {
16287 name: 'text-margin-y',
16288 type: t.bidirectionalSize,
16289 triggersBounds: diff.any
16290 }];
16291 var sourceLabel = [{
16292 name: 'source-label',
16293 type: t.text,
16294 triggersBounds: diff.any
16295 }, {
16296 name: 'source-text-rotation',
16297 type: t.textRotation,
16298 triggersBounds: diff.any
16299 }, {
16300 name: 'source-text-margin-x',
16301 type: t.bidirectionalSize,
16302 triggersBounds: diff.any
16303 }, {
16304 name: 'source-text-margin-y',
16305 type: t.bidirectionalSize,
16306 triggersBounds: diff.any
16307 }, {
16308 name: 'source-text-offset',
16309 type: t.size,
16310 triggersBounds: diff.any
16311 }];
16312 var targetLabel = [{
16313 name: 'target-label',
16314 type: t.text,
16315 triggersBounds: diff.any
16316 }, {
16317 name: 'target-text-rotation',
16318 type: t.textRotation,
16319 triggersBounds: diff.any
16320 }, {
16321 name: 'target-text-margin-x',
16322 type: t.bidirectionalSize,
16323 triggersBounds: diff.any
16324 }, {
16325 name: 'target-text-margin-y',
16326 type: t.bidirectionalSize,
16327 triggersBounds: diff.any
16328 }, {
16329 name: 'target-text-offset',
16330 type: t.size,
16331 triggersBounds: diff.any
16332 }];
16333 var labelDimensions = [{
16334 name: 'font-family',
16335 type: t.fontFamily,
16336 triggersBounds: diff.any
16337 }, {
16338 name: 'font-style',
16339 type: t.fontStyle,
16340 triggersBounds: diff.any
16341 }, {
16342 name: 'font-weight',
16343 type: t.fontWeight,
16344 triggersBounds: diff.any
16345 }, {
16346 name: 'font-size',
16347 type: t.size,
16348 triggersBounds: diff.any
16349 }, {
16350 name: 'text-transform',
16351 type: t.textTransform,
16352 triggersBounds: diff.any
16353 }, {
16354 name: 'text-wrap',
16355 type: t.textWrap,
16356 triggersBounds: diff.any
16357 }, {
16358 name: 'text-overflow-wrap',
16359 type: t.textOverflowWrap,
16360 triggersBounds: diff.any
16361 }, {
16362 name: 'text-max-width',
16363 type: t.size,
16364 triggersBounds: diff.any
16365 }, {
16366 name: 'text-outline-width',
16367 type: t.size,
16368 triggersBounds: diff.any
16369 }, {
16370 name: 'line-height',
16371 type: t.positiveNumber,
16372 triggersBounds: diff.any
16373 }];
16374 var commonLabel = [{
16375 name: 'text-valign',
16376 type: t.valign,
16377 triggersBounds: diff.any
16378 }, {
16379 name: 'text-halign',
16380 type: t.halign,
16381 triggersBounds: diff.any
16382 }, {
16383 name: 'color',
16384 type: t.color
16385 }, {
16386 name: 'text-outline-color',
16387 type: t.color
16388 }, {
16389 name: 'text-outline-opacity',
16390 type: t.zeroOneNumber
16391 }, {
16392 name: 'text-background-color',
16393 type: t.color
16394 }, {
16395 name: 'text-background-opacity',
16396 type: t.zeroOneNumber
16397 }, {
16398 name: 'text-background-padding',
16399 type: t.size,
16400 triggersBounds: diff.any
16401 }, {
16402 name: 'text-border-opacity',
16403 type: t.zeroOneNumber
16404 }, {
16405 name: 'text-border-color',
16406 type: t.color
16407 }, {
16408 name: 'text-border-width',
16409 type: t.size,
16410 triggersBounds: diff.any
16411 }, {
16412 name: 'text-border-style',
16413 type: t.borderStyle,
16414 triggersBounds: diff.any
16415 }, {
16416 name: 'text-background-shape',
16417 type: t.textBackgroundShape,
16418 triggersBounds: diff.any
16419 }, {
16420 name: 'text-justification',
16421 type: t.justification
16422 }];
16423 var behavior = [{
16424 name: 'events',
16425 type: t.bool
16426 }, {
16427 name: 'text-events',
16428 type: t.bool
16429 }];
16430 var visibility = [{
16431 name: 'display',
16432 type: t.display,
16433 triggersZOrder: diff.any,
16434 triggersBounds: diff.any,
16435 triggersBoundsOfParallelBeziers: true
16436 }, {
16437 name: 'visibility',
16438 type: t.visibility,
16439 triggersZOrder: diff.any
16440 }, {
16441 name: 'opacity',
16442 type: t.zeroOneNumber,
16443 triggersZOrder: diff.zeroNonZero
16444 }, {
16445 name: 'text-opacity',
16446 type: t.zeroOneNumber
16447 }, {
16448 name: 'min-zoomed-font-size',
16449 type: t.size
16450 }, {
16451 name: 'z-compound-depth',
16452 type: t.zCompoundDepth,
16453 triggersZOrder: diff.any
16454 }, {
16455 name: 'z-index-compare',
16456 type: t.zIndexCompare,
16457 triggersZOrder: diff.any
16458 }, {
16459 name: 'z-index',
16460 type: t.nonNegativeInt,
16461 triggersZOrder: diff.any
16462 }];
16463 var overlay = [{
16464 name: 'overlay-padding',
16465 type: t.size,
16466 triggersBounds: diff.any
16467 }, {
16468 name: 'overlay-color',
16469 type: t.color
16470 }, {
16471 name: 'overlay-opacity',
16472 type: t.zeroOneNumber,
16473 triggersBounds: diff.zeroNonZero
16474 }];
16475 var transition = [{
16476 name: 'transition-property',
16477 type: t.propList
16478 }, {
16479 name: 'transition-duration',
16480 type: t.time
16481 }, {
16482 name: 'transition-delay',
16483 type: t.time
16484 }, {
16485 name: 'transition-timing-function',
16486 type: t.easing
16487 }];
16488
16489 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16490 if (parsedProp.value === 'label') {
16491 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16492 } else {
16493 return parsedProp.pfValue;
16494 }
16495 };
16496
16497 var nodeBody = [{
16498 name: 'height',
16499 type: t.nodeSize,
16500 triggersBounds: diff.any,
16501 hashOverride: nodeSizeHashOverride
16502 }, {
16503 name: 'width',
16504 type: t.nodeSize,
16505 triggersBounds: diff.any,
16506 hashOverride: nodeSizeHashOverride
16507 }, {
16508 name: 'shape',
16509 type: t.nodeShape,
16510 triggersBounds: diff.any
16511 }, {
16512 name: 'shape-polygon-points',
16513 type: t.polygonPointList,
16514 triggersBounds: diff.any
16515 }, {
16516 name: 'background-color',
16517 type: t.color
16518 }, {
16519 name: 'background-fill',
16520 type: t.fill
16521 }, {
16522 name: 'background-opacity',
16523 type: t.zeroOneNumber
16524 }, {
16525 name: 'background-blacken',
16526 type: t.nOneOneNumber
16527 }, {
16528 name: 'background-gradient-stop-colors',
16529 type: t.colors
16530 }, {
16531 name: 'background-gradient-stop-positions',
16532 type: t.percentages
16533 }, {
16534 name: 'background-gradient-direction',
16535 type: t.gradientDirection
16536 }, {
16537 name: 'padding',
16538 type: t.sizeMaybePercent,
16539 triggersBounds: diff.any
16540 }, {
16541 name: 'padding-relative-to',
16542 type: t.paddingRelativeTo,
16543 triggersBounds: diff.any
16544 }, {
16545 name: 'bounds-expansion',
16546 type: t.boundsExpansion,
16547 triggersBounds: diff.any
16548 }];
16549 var nodeBorder = [{
16550 name: 'border-color',
16551 type: t.color
16552 }, {
16553 name: 'border-opacity',
16554 type: t.zeroOneNumber
16555 }, {
16556 name: 'border-width',
16557 type: t.size,
16558 triggersBounds: diff.any
16559 }, {
16560 name: 'border-style',
16561 type: t.borderStyle
16562 }];
16563 var backgroundImage = [{
16564 name: 'background-image',
16565 type: t.urls
16566 }, {
16567 name: 'background-image-crossorigin',
16568 type: t.bgCrossOrigin
16569 }, {
16570 name: 'background-image-opacity',
16571 type: t.zeroOneNumbers
16572 }, {
16573 name: 'background-position-x',
16574 type: t.bgPos
16575 }, {
16576 name: 'background-position-y',
16577 type: t.bgPos
16578 }, {
16579 name: 'background-width-relative-to',
16580 type: t.bgRelativeTo
16581 }, {
16582 name: 'background-height-relative-to',
16583 type: t.bgRelativeTo
16584 }, {
16585 name: 'background-repeat',
16586 type: t.bgRepeat
16587 }, {
16588 name: 'background-fit',
16589 type: t.bgFit
16590 }, {
16591 name: 'background-clip',
16592 type: t.bgClip
16593 }, {
16594 name: 'background-width',
16595 type: t.bgWH
16596 }, {
16597 name: 'background-height',
16598 type: t.bgWH
16599 }, {
16600 name: 'background-offset-x',
16601 type: t.bgPos
16602 }, {
16603 name: 'background-offset-y',
16604 type: t.bgPos
16605 }];
16606 var compound = [{
16607 name: 'position',
16608 type: t.position,
16609 triggersBounds: diff.any
16610 }, {
16611 name: 'compound-sizing-wrt-labels',
16612 type: t.compoundIncludeLabels,
16613 triggersBounds: diff.any
16614 }, {
16615 name: 'min-width',
16616 type: t.size,
16617 triggersBounds: diff.any
16618 }, {
16619 name: 'min-width-bias-left',
16620 type: t.sizeMaybePercent,
16621 triggersBounds: diff.any
16622 }, {
16623 name: 'min-width-bias-right',
16624 type: t.sizeMaybePercent,
16625 triggersBounds: diff.any
16626 }, {
16627 name: 'min-height',
16628 type: t.size,
16629 triggersBounds: diff.any
16630 }, {
16631 name: 'min-height-bias-top',
16632 type: t.sizeMaybePercent,
16633 triggersBounds: diff.any
16634 }, {
16635 name: 'min-height-bias-bottom',
16636 type: t.sizeMaybePercent,
16637 triggersBounds: diff.any
16638 }];
16639 var edgeLine = [{
16640 name: 'line-style',
16641 type: t.lineStyle
16642 }, {
16643 name: 'line-color',
16644 type: t.color
16645 }, {
16646 name: 'line-fill',
16647 type: t.fill
16648 }, {
16649 name: 'line-cap',
16650 type: t.lineCap
16651 }, {
16652 name: 'line-dash-pattern',
16653 type: t.numbers
16654 }, {
16655 name: 'line-dash-offset',
16656 type: t.number
16657 }, {
16658 name: 'line-gradient-stop-colors',
16659 type: t.colors
16660 }, {
16661 name: 'line-gradient-stop-positions',
16662 type: t.percentages
16663 }, {
16664 name: 'curve-style',
16665 type: t.curveStyle,
16666 triggersBounds: diff.any,
16667 triggersBoundsOfParallelBeziers: true
16668 }, {
16669 name: 'haystack-radius',
16670 type: t.zeroOneNumber,
16671 triggersBounds: diff.any
16672 }, {
16673 name: 'source-endpoint',
16674 type: t.edgeEndpoint,
16675 triggersBounds: diff.any
16676 }, {
16677 name: 'target-endpoint',
16678 type: t.edgeEndpoint,
16679 triggersBounds: diff.any
16680 }, {
16681 name: 'control-point-step-size',
16682 type: t.size,
16683 triggersBounds: diff.any
16684 }, {
16685 name: 'control-point-distances',
16686 type: t.bidirectionalSizes,
16687 triggersBounds: diff.any
16688 }, {
16689 name: 'control-point-weights',
16690 type: t.numbers,
16691 triggersBounds: diff.any
16692 }, {
16693 name: 'segment-distances',
16694 type: t.bidirectionalSizes,
16695 triggersBounds: diff.any
16696 }, {
16697 name: 'segment-weights',
16698 type: t.numbers,
16699 triggersBounds: diff.any
16700 }, {
16701 name: 'taxi-turn',
16702 type: t.bidirectionalSizeMaybePercent,
16703 triggersBounds: diff.any
16704 }, {
16705 name: 'taxi-turn-min-distance',
16706 type: t.size,
16707 triggersBounds: diff.any
16708 }, {
16709 name: 'taxi-direction',
16710 type: t.axisDirection,
16711 triggersBounds: diff.any
16712 }, {
16713 name: 'edge-distances',
16714 type: t.edgeDistances,
16715 triggersBounds: diff.any
16716 }, {
16717 name: 'arrow-scale',
16718 type: t.positiveNumber,
16719 triggersBounds: diff.any
16720 }, {
16721 name: 'loop-direction',
16722 type: t.angle,
16723 triggersBounds: diff.any
16724 }, {
16725 name: 'loop-sweep',
16726 type: t.angle,
16727 triggersBounds: diff.any
16728 }, {
16729 name: 'source-distance-from-node',
16730 type: t.size,
16731 triggersBounds: diff.any
16732 }, {
16733 name: 'target-distance-from-node',
16734 type: t.size,
16735 triggersBounds: diff.any
16736 }];
16737 var ghost = [{
16738 name: 'ghost',
16739 type: t.bool,
16740 triggersBounds: diff.any
16741 }, {
16742 name: 'ghost-offset-x',
16743 type: t.bidirectionalSize,
16744 triggersBounds: diff.any
16745 }, {
16746 name: 'ghost-offset-y',
16747 type: t.bidirectionalSize,
16748 triggersBounds: diff.any
16749 }, {
16750 name: 'ghost-opacity',
16751 type: t.zeroOneNumber
16752 }];
16753 var core = [{
16754 name: 'selection-box-color',
16755 type: t.color
16756 }, {
16757 name: 'selection-box-opacity',
16758 type: t.zeroOneNumber
16759 }, {
16760 name: 'selection-box-border-color',
16761 type: t.color
16762 }, {
16763 name: 'selection-box-border-width',
16764 type: t.size
16765 }, {
16766 name: 'active-bg-color',
16767 type: t.color
16768 }, {
16769 name: 'active-bg-opacity',
16770 type: t.zeroOneNumber
16771 }, {
16772 name: 'active-bg-size',
16773 type: t.size
16774 }, {
16775 name: 'outside-texture-bg-color',
16776 type: t.color
16777 }, {
16778 name: 'outside-texture-bg-opacity',
16779 type: t.zeroOneNumber
16780 }]; // pie backgrounds for nodes
16781
16782 var pie = [];
16783 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
16784
16785 pie.push({
16786 name: 'pie-size',
16787 type: t.sizeMaybePercent
16788 });
16789
16790 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
16791 pie.push({
16792 name: 'pie-' + i + '-background-color',
16793 type: t.color
16794 });
16795 pie.push({
16796 name: 'pie-' + i + '-background-size',
16797 type: t.percent
16798 });
16799 pie.push({
16800 name: 'pie-' + i + '-background-opacity',
16801 type: t.zeroOneNumber
16802 });
16803 } // edge arrows
16804
16805
16806 var edgeArrow = [];
16807 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
16808 [{
16809 name: 'arrow-shape',
16810 type: t.arrowShape,
16811 triggersBounds: diff.any
16812 }, {
16813 name: 'arrow-color',
16814 type: t.color
16815 }, {
16816 name: 'arrow-fill',
16817 type: t.arrowFill
16818 }].forEach(function (prop) {
16819 arrowPrefixes.forEach(function (prefix) {
16820 var name = prefix + '-' + prop.name;
16821 var type = prop.type,
16822 triggersBounds = prop.triggersBounds;
16823 edgeArrow.push({
16824 name: name,
16825 type: type,
16826 triggersBounds: triggersBounds
16827 });
16828 });
16829 }, {});
16830 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
16831 var propGroups = styfn$6.propertyGroups = {
16832 // common to all eles
16833 behavior: behavior,
16834 transition: transition,
16835 visibility: visibility,
16836 overlay: overlay,
16837 ghost: ghost,
16838 // labels
16839 commonLabel: commonLabel,
16840 labelDimensions: labelDimensions,
16841 mainLabel: mainLabel,
16842 sourceLabel: sourceLabel,
16843 targetLabel: targetLabel,
16844 // node props
16845 nodeBody: nodeBody,
16846 nodeBorder: nodeBorder,
16847 backgroundImage: backgroundImage,
16848 pie: pie,
16849 compound: compound,
16850 // edge props
16851 edgeLine: edgeLine,
16852 edgeArrow: edgeArrow,
16853 core: core
16854 };
16855 var propGroupNames = styfn$6.propertyGroupNames = {};
16856 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
16857 propGroupKeys.forEach(function (key) {
16858 propGroupNames[key] = propGroups[key].map(function (prop) {
16859 return prop.name;
16860 });
16861 propGroups[key].forEach(function (prop) {
16862 return prop.groupKey = key;
16863 });
16864 }); // define aliases
16865
16866 var aliases = styfn$6.aliases = [{
16867 name: 'content',
16868 pointsTo: 'label'
16869 }, {
16870 name: 'control-point-distance',
16871 pointsTo: 'control-point-distances'
16872 }, {
16873 name: 'control-point-weight',
16874 pointsTo: 'control-point-weights'
16875 }, {
16876 name: 'edge-text-rotation',
16877 pointsTo: 'text-rotation'
16878 }, {
16879 name: 'padding-left',
16880 pointsTo: 'padding'
16881 }, {
16882 name: 'padding-right',
16883 pointsTo: 'padding'
16884 }, {
16885 name: 'padding-top',
16886 pointsTo: 'padding'
16887 }, {
16888 name: 'padding-bottom',
16889 pointsTo: 'padding'
16890 }]; // list of property names
16891
16892 styfn$6.propertyNames = props.map(function (p) {
16893 return p.name;
16894 }); // allow access of properties by name ( e.g. style.properties.height )
16895
16896 for (var _i = 0; _i < props.length; _i++) {
16897 var prop = props[_i];
16898 props[prop.name] = prop; // allow lookup by name
16899 } // map aliases
16900
16901
16902 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
16903 var alias = aliases[_i2];
16904 var pointsToProp = props[alias.pointsTo];
16905 var aliasProp = {
16906 name: alias.name,
16907 alias: true,
16908 pointsTo: pointsToProp
16909 }; // add alias prop for parsing
16910
16911 props.push(aliasProp);
16912 props[alias.name] = aliasProp; // allow lookup by name
16913 }
16914})();
16915
16916styfn$6.getDefaultProperty = function (name) {
16917 return this.getDefaultProperties()[name];
16918};
16919
16920styfn$6.getDefaultProperties = function () {
16921 var _p = this._private;
16922
16923 if (_p.defaultProperties != null) {
16924 return _p.defaultProperties;
16925 }
16926
16927 var rawProps = extend({
16928 // core props
16929 'selection-box-color': '#ddd',
16930 'selection-box-opacity': 0.65,
16931 'selection-box-border-color': '#aaa',
16932 'selection-box-border-width': 1,
16933 'active-bg-color': 'black',
16934 'active-bg-opacity': 0.15,
16935 'active-bg-size': 30,
16936 'outside-texture-bg-color': '#000',
16937 'outside-texture-bg-opacity': 0.125,
16938 // common node/edge props
16939 'events': 'yes',
16940 'text-events': 'no',
16941 'text-valign': 'top',
16942 'text-halign': 'center',
16943 'text-justification': 'auto',
16944 'line-height': 1,
16945 'color': '#000',
16946 'text-outline-color': '#000',
16947 'text-outline-width': 0,
16948 'text-outline-opacity': 1,
16949 'text-opacity': 1,
16950 'text-decoration': 'none',
16951 'text-transform': 'none',
16952 'text-wrap': 'none',
16953 'text-overflow-wrap': 'whitespace',
16954 'text-max-width': 9999,
16955 'text-background-color': '#000',
16956 'text-background-opacity': 0,
16957 'text-background-shape': 'rectangle',
16958 'text-background-padding': 0,
16959 'text-border-opacity': 0,
16960 'text-border-width': 0,
16961 'text-border-style': 'solid',
16962 'text-border-color': '#000',
16963 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
16964 'font-style': 'normal',
16965 'font-weight': 'normal',
16966 'font-size': 16,
16967 'min-zoomed-font-size': 0,
16968 'text-rotation': 'none',
16969 'source-text-rotation': 'none',
16970 'target-text-rotation': 'none',
16971 'visibility': 'visible',
16972 'display': 'element',
16973 'opacity': 1,
16974 'z-compound-depth': 'auto',
16975 'z-index-compare': 'auto',
16976 'z-index': 0,
16977 'label': '',
16978 'text-margin-x': 0,
16979 'text-margin-y': 0,
16980 'source-label': '',
16981 'source-text-offset': 0,
16982 'source-text-margin-x': 0,
16983 'source-text-margin-y': 0,
16984 'target-label': '',
16985 'target-text-offset': 0,
16986 'target-text-margin-x': 0,
16987 'target-text-margin-y': 0,
16988 'overlay-opacity': 0,
16989 'overlay-color': '#000',
16990 'overlay-padding': 10,
16991 'transition-property': 'none',
16992 'transition-duration': 0,
16993 'transition-delay': 0,
16994 'transition-timing-function': 'linear',
16995 // node props
16996 'background-blacken': 0,
16997 'background-color': '#999',
16998 'background-fill': 'solid',
16999 'background-opacity': 1,
17000 'background-image': 'none',
17001 'background-image-crossorigin': 'anonymous',
17002 'background-image-opacity': 1,
17003 'background-position-x': '50%',
17004 'background-position-y': '50%',
17005 'background-offset-x': 0,
17006 'background-offset-y': 0,
17007 'background-width-relative-to': 'include-padding',
17008 'background-height-relative-to': 'include-padding',
17009 'background-repeat': 'no-repeat',
17010 'background-fit': 'none',
17011 'background-clip': 'node',
17012 'background-width': 'auto',
17013 'background-height': 'auto',
17014 'border-color': '#000',
17015 'border-opacity': 1,
17016 'border-width': 0,
17017 'border-style': 'solid',
17018 'height': 30,
17019 'width': 30,
17020 'shape': 'ellipse',
17021 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17022 'bounds-expansion': 0,
17023 // node gradient
17024 'background-gradient-direction': 'to-bottom',
17025 'background-gradient-stop-colors': '#999',
17026 'background-gradient-stop-positions': '0%',
17027 // ghost props
17028 'ghost': 'no',
17029 'ghost-offset-y': 0,
17030 'ghost-offset-x': 0,
17031 'ghost-opacity': 0,
17032 // compound props
17033 'padding': 0,
17034 'padding-relative-to': 'width',
17035 'position': 'origin',
17036 'compound-sizing-wrt-labels': 'include',
17037 'min-width': 0,
17038 'min-width-bias-left': 0,
17039 'min-width-bias-right': 0,
17040 'min-height': 0,
17041 'min-height-bias-top': 0,
17042 'min-height-bias-bottom': 0
17043 }, {
17044 // node pie bg
17045 'pie-size': '100%'
17046 }, [{
17047 name: 'pie-{{i}}-background-color',
17048 value: 'black'
17049 }, {
17050 name: 'pie-{{i}}-background-size',
17051 value: '0%'
17052 }, {
17053 name: 'pie-{{i}}-background-opacity',
17054 value: 1
17055 }].reduce(function (css, prop) {
17056 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17057 var name = prop.name.replace('{{i}}', i);
17058 var val = prop.value;
17059 css[name] = val;
17060 }
17061
17062 return css;
17063 }, {}), {
17064 // edge props
17065 'line-style': 'solid',
17066 'line-color': '#999',
17067 'line-fill': 'solid',
17068 'line-cap': 'butt',
17069 'line-gradient-stop-colors': '#999',
17070 'line-gradient-stop-positions': '0%',
17071 'control-point-step-size': 40,
17072 'control-point-weights': 0.5,
17073 'segment-weights': 0.5,
17074 'segment-distances': 20,
17075 'taxi-turn': '50%',
17076 'taxi-turn-min-distance': 10,
17077 'taxi-direction': 'auto',
17078 'edge-distances': 'intersection',
17079 'curve-style': 'haystack',
17080 'haystack-radius': 0,
17081 'arrow-scale': 1,
17082 'loop-direction': '-45deg',
17083 'loop-sweep': '-90deg',
17084 'source-distance-from-node': 0,
17085 'target-distance-from-node': 0,
17086 'source-endpoint': 'outside-to-node',
17087 'target-endpoint': 'outside-to-node',
17088 'line-dash-pattern': [6, 3],
17089 'line-dash-offset': 0
17090 }, [{
17091 name: 'arrow-shape',
17092 value: 'none'
17093 }, {
17094 name: 'arrow-color',
17095 value: '#999'
17096 }, {
17097 name: 'arrow-fill',
17098 value: 'filled'
17099 }].reduce(function (css, prop) {
17100 styfn$6.arrowPrefixes.forEach(function (prefix) {
17101 var name = prefix + '-' + prop.name;
17102 var val = prop.value;
17103 css[name] = val;
17104 });
17105 return css;
17106 }, {}));
17107 var parsedProps = {};
17108
17109 for (var i = 0; i < this.properties.length; i++) {
17110 var prop = this.properties[i];
17111
17112 if (prop.pointsTo) {
17113 continue;
17114 }
17115
17116 var name = prop.name;
17117 var val = rawProps[name];
17118 var parsedProp = this.parse(name, val);
17119 parsedProps[name] = parsedProp;
17120 }
17121
17122 _p.defaultProperties = parsedProps;
17123 return _p.defaultProperties;
17124};
17125
17126styfn$6.addDefaultStylesheet = function () {
17127 this.selector(':parent').css({
17128 'shape': 'rectangle',
17129 'padding': 10,
17130 'background-color': '#eee',
17131 'border-color': '#ccc',
17132 'border-width': 1
17133 }).selector('edge').css({
17134 'width': 3
17135 }).selector(':loop').css({
17136 'curve-style': 'bezier'
17137 }).selector('edge:compound').css({
17138 'curve-style': 'bezier',
17139 'source-endpoint': 'outside-to-line',
17140 'target-endpoint': 'outside-to-line'
17141 }).selector(':selected').css({
17142 'background-color': '#0169D9',
17143 'line-color': '#0169D9',
17144 'source-arrow-color': '#0169D9',
17145 'target-arrow-color': '#0169D9',
17146 'mid-source-arrow-color': '#0169D9',
17147 'mid-target-arrow-color': '#0169D9'
17148 }).selector(':parent:selected').css({
17149 'background-color': '#CCE1F9',
17150 'border-color': '#aec8e5'
17151 }).selector(':active').css({
17152 'overlay-color': 'black',
17153 'overlay-padding': 10,
17154 'overlay-opacity': 0.25
17155 });
17156 this.defaultLength = this.length;
17157};
17158
17159var styfn$7 = {}; // a caching layer for property parsing
17160
17161styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
17162 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17163
17164 if (fn(value)) {
17165 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17166 }
17167
17168 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17169 var bypassKey = propIsBypass ? 't' : 'f';
17170 var valueKey = '' + value;
17171 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17172 var propCache = self.propCache = self.propCache || [];
17173 var ret;
17174
17175 if (!(ret = propCache[argHash])) {
17176 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17177 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17178 // - mappings can't be shared b/c mappings are per-element
17179
17180
17181 if (propIsBypass || propIsFlat === 'mapping') {
17182 // need a copy since props are mutated later in their lifecycles
17183 ret = copy(ret);
17184
17185 if (ret) {
17186 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17187 }
17188 }
17189
17190 return ret;
17191};
17192
17193styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17194 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17195
17196 if (!prop && value != null) {
17197 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17198 }
17199
17200 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17201 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17202 }
17203
17204 return prop;
17205}; // parse a property; return null on invalid; return parsed property otherwise
17206// fields :
17207// - name : the name of the property
17208// - value : the parsed, native-typed value of the property
17209// - strValue : a string value that represents the property value in valid css
17210// - bypass : true iff the property is a bypass property
17211
17212
17213styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17214 var self = this;
17215 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17216
17217 var property = self.properties[name];
17218 var passedValue = value;
17219 var types = self.types;
17220
17221 if (!property) {
17222 return null;
17223 } // return null on property of unknown name
17224
17225
17226 if (value === undefined) {
17227 return null;
17228 } // can't assign undefined
17229 // the property may be an alias
17230
17231
17232 if (property.alias) {
17233 property = property.pointsTo;
17234 name = property.name;
17235 }
17236
17237 var valueIsString = string(value);
17238
17239 if (valueIsString) {
17240 // trim the value to make parsing easier
17241 value = value.trim();
17242 }
17243
17244 var type = property.type;
17245
17246 if (!type) {
17247 return null;
17248 } // no type, no luck
17249 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17250
17251
17252 if (propIsBypass && (value === '' || value === null)) {
17253 return {
17254 name: name,
17255 value: value,
17256 bypass: true,
17257 deleteBypass: true
17258 };
17259 } // check if value is a function used as a mapper
17260
17261
17262 if (fn(value)) {
17263 return {
17264 name: name,
17265 value: value,
17266 strValue: 'fn',
17267 mapped: types.fn,
17268 bypass: propIsBypass
17269 };
17270 } // check if value is mapped
17271
17272
17273 var data, mapData;
17274
17275 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))) {
17276 if (propIsBypass) {
17277 return false;
17278 } // mappers not allowed in bypass
17279
17280
17281 var mapped = types.data;
17282 return {
17283 name: name,
17284 value: data,
17285 strValue: '' + value,
17286 mapped: mapped,
17287 field: data[1],
17288 bypass: propIsBypass
17289 };
17290 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17291 if (propIsBypass) {
17292 return false;
17293 } // mappers not allowed in bypass
17294
17295
17296 if (type.multiple) {
17297 return false;
17298 } // impossible to map to num
17299
17300
17301 var _mapped = types.mapData; // we can map only if the type is a colour or a number
17302
17303 if (!(type.color || type.number)) {
17304 return false;
17305 }
17306
17307 var valueMin = this.parse(name, mapData[4]); // parse to validate
17308
17309 if (!valueMin || valueMin.mapped) {
17310 return false;
17311 } // can't be invalid or mapped
17312
17313
17314 var valueMax = this.parse(name, mapData[5]); // parse to validate
17315
17316 if (!valueMax || valueMax.mapped) {
17317 return false;
17318 } // can't be invalid or mapped
17319 // check if valueMin and valueMax are the same
17320
17321
17322 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17323 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17324 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17325 } else if (type.color) {
17326 var c1 = valueMin.value;
17327 var c2 = valueMax.value;
17328 var same = c1[0] === c2[0] // red
17329 && c1[1] === c2[1] // green
17330 && c1[2] === c2[2] // blue
17331 && ( // optional alpha
17332 c1[3] === c2[3] // same alpha outright
17333 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
17334 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17335 );
17336
17337 if (same) {
17338 return false;
17339 } // can't make a mapper without a range
17340
17341 }
17342
17343 return {
17344 name: name,
17345 value: mapData,
17346 strValue: '' + value,
17347 mapped: _mapped,
17348 field: mapData[1],
17349 fieldMin: parseFloat(mapData[2]),
17350 // min & max are numeric
17351 fieldMax: parseFloat(mapData[3]),
17352 valueMin: valueMin.value,
17353 valueMax: valueMax.value,
17354 bypass: propIsBypass
17355 };
17356 }
17357
17358 if (type.multiple && propIsFlat !== 'multiple') {
17359 var vals;
17360
17361 if (valueIsString) {
17362 vals = value.split(/\s+/);
17363 } else if (array(value)) {
17364 vals = value;
17365 } else {
17366 vals = [value];
17367 }
17368
17369 if (type.evenMultiple && vals.length % 2 !== 0) {
17370 return null;
17371 }
17372
17373 var valArr = [];
17374 var unitsArr = [];
17375 var pfValArr = [];
17376 var strVal = '';
17377 var hasEnum = false;
17378
17379 for (var i = 0; i < vals.length; i++) {
17380 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17381 hasEnum = hasEnum || string(p.value);
17382 valArr.push(p.value);
17383 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17384 unitsArr.push(p.units);
17385 strVal += (i > 0 ? ' ' : '') + p.strValue;
17386 }
17387
17388 if (type.validate && !type.validate(valArr, unitsArr)) {
17389 return null;
17390 }
17391
17392 if (type.singleEnum && hasEnum) {
17393 if (valArr.length === 1 && string(valArr[0])) {
17394 return {
17395 name: name,
17396 value: valArr[0],
17397 strValue: valArr[0],
17398 bypass: propIsBypass
17399 };
17400 } else {
17401 return null;
17402 }
17403 }
17404
17405 return {
17406 name: name,
17407 value: valArr,
17408 pfValue: pfValArr,
17409 strValue: strVal,
17410 bypass: propIsBypass,
17411 units: unitsArr
17412 };
17413 } // several types also allow enums
17414
17415
17416 var checkEnums = function checkEnums() {
17417 for (var _i = 0; _i < type.enums.length; _i++) {
17418 var en = type.enums[_i];
17419
17420 if (en === value) {
17421 return {
17422 name: name,
17423 value: value,
17424 strValue: '' + value,
17425 bypass: propIsBypass
17426 };
17427 }
17428 }
17429
17430 return null;
17431 }; // check the type and return the appropriate object
17432
17433
17434 if (type.number) {
17435 var units;
17436 var implicitUnits = 'px'; // not set => px
17437
17438 if (type.units) {
17439 // use specified units if set
17440 units = type.units;
17441 }
17442
17443 if (type.implicitUnits) {
17444 implicitUnits = type.implicitUnits;
17445 }
17446
17447 if (!type.unitless) {
17448 if (valueIsString) {
17449 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17450
17451 if (units) {
17452 unitsRegex = units;
17453 } // only allow explicit units if so set
17454
17455
17456 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
17457
17458 if (match) {
17459 value = match[1];
17460 units = match[2] || implicitUnits;
17461 }
17462 } else if (!units || type.implicitUnits) {
17463 units = implicitUnits; // implicitly px if unspecified
17464 }
17465 }
17466
17467 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
17468
17469 if (isNaN(value) && type.enums === undefined) {
17470 return null;
17471 } // check if this number type also accepts special keywords in place of numbers
17472 // (i.e. `left`, `auto`, etc)
17473
17474
17475 if (isNaN(value) && type.enums !== undefined) {
17476 value = passedValue;
17477 return checkEnums();
17478 } // check if value must be an integer
17479
17480
17481 if (type.integer && !integer(value)) {
17482 return null;
17483 } // check value is within range
17484
17485
17486 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17487 return null;
17488 }
17489
17490 var ret = {
17491 name: name,
17492 value: value,
17493 strValue: '' + value + (units ? units : ''),
17494 units: units,
17495 bypass: propIsBypass
17496 }; // normalise value in pixels
17497
17498 if (type.unitless || units !== 'px' && units !== 'em') {
17499 ret.pfValue = value;
17500 } else {
17501 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17502 } // normalise value in ms
17503
17504
17505 if (units === 'ms' || units === 's') {
17506 ret.pfValue = units === 'ms' ? value : 1000 * value;
17507 } // normalise value in rad
17508
17509
17510 if (units === 'deg' || units === 'rad') {
17511 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17512 } // normalize value in %
17513
17514
17515 if (units === '%') {
17516 ret.pfValue = value / 100;
17517 }
17518
17519 return ret;
17520 } else if (type.propList) {
17521 var props = [];
17522 var propsStr = '' + value;
17523
17524 if (propsStr === 'none') ; else {
17525 // go over each prop
17526 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17527
17528 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17529 var propName = propsSplit[_i2].trim();
17530
17531 if (self.properties[propName]) {
17532 props.push(propName);
17533 } else {
17534 warn('`' + propName + '` is not a valid property name');
17535 }
17536 }
17537
17538 if (props.length === 0) {
17539 return null;
17540 }
17541 }
17542
17543 return {
17544 name: name,
17545 value: props,
17546 strValue: props.length === 0 ? 'none' : props.join(' '),
17547 bypass: propIsBypass
17548 };
17549 } else if (type.color) {
17550 var tuple = color2tuple(value);
17551
17552 if (!tuple) {
17553 return null;
17554 }
17555
17556 return {
17557 name: name,
17558 value: tuple,
17559 pfValue: tuple,
17560 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
17561 // n.b. no spaces b/c of multiple support
17562 bypass: propIsBypass
17563 };
17564 } else if (type.regex || type.regexes) {
17565 // first check enums
17566 if (type.enums) {
17567 var enumProp = checkEnums();
17568
17569 if (enumProp) {
17570 return enumProp;
17571 }
17572 }
17573
17574 var regexes = type.regexes ? type.regexes : [type.regex];
17575
17576 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
17577 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
17578
17579 var m = regex.exec(value);
17580
17581 if (m) {
17582 // regex matches
17583 return {
17584 name: name,
17585 value: type.singleRegexMatchValue ? m[1] : m,
17586 strValue: '' + value,
17587 bypass: propIsBypass
17588 };
17589 }
17590 }
17591
17592 return null; // didn't match any
17593 } else if (type.string) {
17594 // just return
17595 return {
17596 name: name,
17597 value: '' + value,
17598 strValue: '' + value,
17599 bypass: propIsBypass
17600 };
17601 } else if (type.enums) {
17602 // check enums last because it's a combo type in others
17603 return checkEnums();
17604 } else {
17605 return null; // not a type we can handle
17606 }
17607};
17608
17609var Style = function Style(cy) {
17610 if (!(this instanceof Style)) {
17611 return new Style(cy);
17612 }
17613
17614 if (!core(cy)) {
17615 error('A style must have a core reference');
17616 return;
17617 }
17618
17619 this._private = {
17620 cy: cy,
17621 coreStyle: {}
17622 };
17623 this.length = 0;
17624 this.resetToDefault();
17625};
17626
17627var styfn$8 = Style.prototype;
17628
17629styfn$8.instanceString = function () {
17630 return 'style';
17631}; // remove all contexts
17632
17633
17634styfn$8.clear = function () {
17635 for (var i = 0; i < this.length; i++) {
17636 this[i] = undefined;
17637 }
17638
17639 this.length = 0;
17640 var _p = this._private;
17641 _p.newStyle = true;
17642 return this; // chaining
17643};
17644
17645styfn$8.resetToDefault = function () {
17646 this.clear();
17647 this.addDefaultStylesheet();
17648 return this;
17649}; // builds a style object for the 'core' selector
17650
17651
17652styfn$8.core = function (propName) {
17653 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
17654}; // create a new context from the specified selector string and switch to that context
17655
17656
17657styfn$8.selector = function (selectorStr) {
17658 // 'core' is a special case and does not need a selector
17659 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
17660 var i = this.length++; // new context means new index
17661
17662 this[i] = {
17663 selector: selector,
17664 properties: [],
17665 mappedProperties: [],
17666 index: i
17667 };
17668 return this; // chaining
17669}; // add one or many css rules to the current context
17670
17671
17672styfn$8.css = function () {
17673 var self = this;
17674 var args = arguments;
17675
17676 if (args.length === 1) {
17677 var map = args[0];
17678
17679 for (var i = 0; i < self.properties.length; i++) {
17680 var prop = self.properties[i];
17681 var mapVal = map[prop.name];
17682
17683 if (mapVal === undefined) {
17684 mapVal = map[dash2camel(prop.name)];
17685 }
17686
17687 if (mapVal !== undefined) {
17688 this.cssRule(prop.name, mapVal);
17689 }
17690 }
17691 } else if (args.length === 2) {
17692 this.cssRule(args[0], args[1]);
17693 } // do nothing if args are invalid
17694
17695
17696 return this; // chaining
17697};
17698
17699styfn$8.style = styfn$8.css; // add a single css rule to the current context
17700
17701styfn$8.cssRule = function (name, value) {
17702 // name-value pair
17703 var property = this.parse(name, value); // add property to current context if valid
17704
17705 if (property) {
17706 var i = this.length - 1;
17707 this[i].properties.push(property);
17708 this[i].properties[property.name] = property; // allow access by name as well
17709
17710 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
17711 this._private.hasPie = true;
17712 }
17713
17714 if (property.mapped) {
17715 this[i].mappedProperties.push(property);
17716 } // add to core style if necessary
17717
17718
17719 var currentSelectorIsCore = !this[i].selector;
17720
17721 if (currentSelectorIsCore) {
17722 this._private.coreStyle[property.name] = property;
17723 }
17724 }
17725
17726 return this; // chaining
17727};
17728
17729styfn$8.append = function (style) {
17730 if (stylesheet(style)) {
17731 style.appendToStyle(this);
17732 } else if (array(style)) {
17733 this.appendFromJson(style);
17734 } else if (string(style)) {
17735 this.appendFromString(style);
17736 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
17737
17738
17739 return this;
17740}; // static function
17741
17742
17743Style.fromJson = function (cy, json) {
17744 var style = new Style(cy);
17745 style.fromJson(json);
17746 return style;
17747};
17748
17749Style.fromString = function (cy, string) {
17750 return new Style(cy).fromString(string);
17751};
17752
17753[styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
17754 extend(styfn$8, props);
17755});
17756Style.types = styfn$8.types;
17757Style.properties = styfn$8.properties;
17758Style.propertyGroups = styfn$8.propertyGroups;
17759Style.propertyGroupNames = styfn$8.propertyGroupNames;
17760Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
17761
17762var corefn$7 = {
17763 style: function style(newStyle) {
17764 if (newStyle) {
17765 var s = this.setStyle(newStyle);
17766 s.update();
17767 }
17768
17769 return this._private.style;
17770 },
17771 setStyle: function setStyle(style) {
17772 var _p = this._private;
17773
17774 if (stylesheet(style)) {
17775 _p.style = style.generateStyle(this);
17776 } else if (array(style)) {
17777 _p.style = Style.fromJson(this, style);
17778 } else if (string(style)) {
17779 _p.style = Style.fromString(this, style);
17780 } else {
17781 _p.style = Style(this);
17782 }
17783
17784 return _p.style;
17785 }
17786};
17787
17788var defaultSelectionType = 'single';
17789var corefn$8 = {
17790 autolock: function autolock(bool) {
17791 if (bool !== undefined) {
17792 this._private.autolock = bool ? true : false;
17793 } else {
17794 return this._private.autolock;
17795 }
17796
17797 return this; // chaining
17798 },
17799 autoungrabify: function autoungrabify(bool) {
17800 if (bool !== undefined) {
17801 this._private.autoungrabify = bool ? true : false;
17802 } else {
17803 return this._private.autoungrabify;
17804 }
17805
17806 return this; // chaining
17807 },
17808 autounselectify: function autounselectify(bool) {
17809 if (bool !== undefined) {
17810 this._private.autounselectify = bool ? true : false;
17811 } else {
17812 return this._private.autounselectify;
17813 }
17814
17815 return this; // chaining
17816 },
17817 selectionType: function selectionType(selType) {
17818 var _p = this._private;
17819
17820 if (_p.selectionType == null) {
17821 _p.selectionType = defaultSelectionType;
17822 }
17823
17824 if (selType !== undefined) {
17825 if (selType === 'additive' || selType === 'single') {
17826 _p.selectionType = selType;
17827 }
17828 } else {
17829 return _p.selectionType;
17830 }
17831
17832 return this;
17833 },
17834 panningEnabled: function panningEnabled(bool) {
17835 if (bool !== undefined) {
17836 this._private.panningEnabled = bool ? true : false;
17837 } else {
17838 return this._private.panningEnabled;
17839 }
17840
17841 return this; // chaining
17842 },
17843 userPanningEnabled: function userPanningEnabled(bool) {
17844 if (bool !== undefined) {
17845 this._private.userPanningEnabled = bool ? true : false;
17846 } else {
17847 return this._private.userPanningEnabled;
17848 }
17849
17850 return this; // chaining
17851 },
17852 zoomingEnabled: function zoomingEnabled(bool) {
17853 if (bool !== undefined) {
17854 this._private.zoomingEnabled = bool ? true : false;
17855 } else {
17856 return this._private.zoomingEnabled;
17857 }
17858
17859 return this; // chaining
17860 },
17861 userZoomingEnabled: function userZoomingEnabled(bool) {
17862 if (bool !== undefined) {
17863 this._private.userZoomingEnabled = bool ? true : false;
17864 } else {
17865 return this._private.userZoomingEnabled;
17866 }
17867
17868 return this; // chaining
17869 },
17870 boxSelectionEnabled: function boxSelectionEnabled(bool) {
17871 if (bool !== undefined) {
17872 this._private.boxSelectionEnabled = bool ? true : false;
17873 } else {
17874 return this._private.boxSelectionEnabled;
17875 }
17876
17877 return this; // chaining
17878 },
17879 pan: function pan() {
17880 var args = arguments;
17881 var pan = this._private.pan;
17882 var dim, val, dims, x, y;
17883
17884 switch (args.length) {
17885 case 0:
17886 // .pan()
17887 return pan;
17888
17889 case 1:
17890 if (string(args[0])) {
17891 // .pan('x')
17892 dim = args[0];
17893 return pan[dim];
17894 } else if (plainObject(args[0])) {
17895 // .pan({ x: 0, y: 100 })
17896 if (!this._private.panningEnabled) {
17897 return this;
17898 }
17899
17900 dims = args[0];
17901 x = dims.x;
17902 y = dims.y;
17903
17904 if (number(x)) {
17905 pan.x = x;
17906 }
17907
17908 if (number(y)) {
17909 pan.y = y;
17910 }
17911
17912 this.emit('pan viewport');
17913 }
17914
17915 break;
17916
17917 case 2:
17918 // .pan('x', 100)
17919 if (!this._private.panningEnabled) {
17920 return this;
17921 }
17922
17923 dim = args[0];
17924 val = args[1];
17925
17926 if ((dim === 'x' || dim === 'y') && number(val)) {
17927 pan[dim] = val;
17928 }
17929
17930 this.emit('pan viewport');
17931 break;
17932 // invalid
17933 }
17934
17935 this.notify('viewport');
17936 return this; // chaining
17937 },
17938 panBy: function panBy(arg0, arg1) {
17939 var args = arguments;
17940 var pan = this._private.pan;
17941 var dim, val, dims, x, y;
17942
17943 if (!this._private.panningEnabled) {
17944 return this;
17945 }
17946
17947 switch (args.length) {
17948 case 1:
17949 if (plainObject(arg0)) {
17950 // .panBy({ x: 0, y: 100 })
17951 dims = args[0];
17952 x = dims.x;
17953 y = dims.y;
17954
17955 if (number(x)) {
17956 pan.x += x;
17957 }
17958
17959 if (number(y)) {
17960 pan.y += y;
17961 }
17962
17963 this.emit('pan viewport');
17964 }
17965
17966 break;
17967
17968 case 2:
17969 // .panBy('x', 100)
17970 dim = arg0;
17971 val = arg1;
17972
17973 if ((dim === 'x' || dim === 'y') && number(val)) {
17974 pan[dim] += val;
17975 }
17976
17977 this.emit('pan viewport');
17978 break;
17979 // invalid
17980 }
17981
17982 this.notify('viewport');
17983 return this; // chaining
17984 },
17985 fit: function fit(elements, padding) {
17986 var viewportState = this.getFitViewport(elements, padding);
17987
17988 if (viewportState) {
17989 var _p = this._private;
17990 _p.zoom = viewportState.zoom;
17991 _p.pan = viewportState.pan;
17992 this.emit('pan zoom viewport');
17993 this.notify('viewport');
17994 }
17995
17996 return this; // chaining
17997 },
17998 getFitViewport: function getFitViewport(elements, padding) {
17999 if (number(elements) && padding === undefined) {
18000 // elements is optional
18001 padding = elements;
18002 elements = undefined;
18003 }
18004
18005 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18006 return;
18007 }
18008
18009 var bb;
18010
18011 if (string(elements)) {
18012 var sel = elements;
18013 elements = this.$(sel);
18014 } else if (boundingBox(elements)) {
18015 // assume bb
18016 var bbe = elements;
18017 bb = {
18018 x1: bbe.x1,
18019 y1: bbe.y1,
18020 x2: bbe.x2,
18021 y2: bbe.y2
18022 };
18023 bb.w = bb.x2 - bb.x1;
18024 bb.h = bb.y2 - bb.y1;
18025 } else if (!elementOrCollection(elements)) {
18026 elements = this.mutableElements();
18027 }
18028
18029 if (elementOrCollection(elements) && elements.empty()) {
18030 return;
18031 } // can't fit to nothing
18032
18033
18034 bb = bb || elements.boundingBox();
18035 var w = this.width();
18036 var h = this.height();
18037 var zoom;
18038 padding = number(padding) ? padding : 0;
18039
18040 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18041 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18042
18043 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18044 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18045 var pan = {
18046 // now pan to middle
18047 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18048 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18049 };
18050 return {
18051 zoom: zoom,
18052 pan: pan
18053 };
18054 }
18055
18056 return;
18057 },
18058 zoomRange: function zoomRange(min, max) {
18059 var _p = this._private;
18060
18061 if (max == null) {
18062 var opts = min;
18063 min = opts.min;
18064 max = opts.max;
18065 }
18066
18067 if (number(min) && number(max) && min <= max) {
18068 _p.minZoom = min;
18069 _p.maxZoom = max;
18070 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
18071 _p.minZoom = min;
18072 } else if (number(max) && min === undefined && max >= _p.minZoom) {
18073 _p.maxZoom = max;
18074 }
18075
18076 return this;
18077 },
18078 minZoom: function minZoom(zoom) {
18079 if (zoom === undefined) {
18080 return this._private.minZoom;
18081 } else {
18082 return this.zoomRange({
18083 min: zoom
18084 });
18085 }
18086 },
18087 maxZoom: function maxZoom(zoom) {
18088 if (zoom === undefined) {
18089 return this._private.maxZoom;
18090 } else {
18091 return this.zoomRange({
18092 max: zoom
18093 });
18094 }
18095 },
18096 getZoomedViewport: function getZoomedViewport(params) {
18097 var _p = this._private;
18098 var currentPan = _p.pan;
18099 var currentZoom = _p.zoom;
18100 var pos; // in rendered px
18101
18102 var zoom;
18103 var bail = false;
18104
18105 if (!_p.zoomingEnabled) {
18106 // zooming disabled
18107 bail = true;
18108 }
18109
18110 if (number(params)) {
18111 // then set the zoom
18112 zoom = params;
18113 } else if (plainObject(params)) {
18114 // then zoom about a point
18115 zoom = params.level;
18116
18117 if (params.position != null) {
18118 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18119 } else if (params.renderedPosition != null) {
18120 pos = params.renderedPosition;
18121 }
18122
18123 if (pos != null && !_p.panningEnabled) {
18124 // panning disabled
18125 bail = true;
18126 }
18127 } // crop zoom
18128
18129
18130 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18131 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18132
18133 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
18134 return null;
18135 }
18136
18137 if (pos != null) {
18138 // set zoom about position
18139 var pan1 = currentPan;
18140 var zoom1 = currentZoom;
18141 var zoom2 = zoom;
18142 var pan2 = {
18143 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18144 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18145 };
18146 return {
18147 zoomed: true,
18148 panned: true,
18149 zoom: zoom2,
18150 pan: pan2
18151 };
18152 } else {
18153 // just set the zoom
18154 return {
18155 zoomed: true,
18156 panned: false,
18157 zoom: zoom,
18158 pan: currentPan
18159 };
18160 }
18161 },
18162 zoom: function zoom(params) {
18163 if (params === undefined) {
18164 // get
18165 return this._private.zoom;
18166 } else {
18167 // set
18168 var vp = this.getZoomedViewport(params);
18169 var _p = this._private;
18170
18171 if (vp == null || !vp.zoomed) {
18172 return this;
18173 }
18174
18175 _p.zoom = vp.zoom;
18176
18177 if (vp.panned) {
18178 _p.pan.x = vp.pan.x;
18179 _p.pan.y = vp.pan.y;
18180 }
18181
18182 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18183 this.notify('viewport');
18184 return this; // chaining
18185 }
18186 },
18187 viewport: function viewport(opts) {
18188 var _p = this._private;
18189 var zoomDefd = true;
18190 var panDefd = true;
18191 var events = []; // to trigger
18192
18193 var zoomFailed = false;
18194 var panFailed = false;
18195
18196 if (!opts) {
18197 return this;
18198 }
18199
18200 if (!number(opts.zoom)) {
18201 zoomDefd = false;
18202 }
18203
18204 if (!plainObject(opts.pan)) {
18205 panDefd = false;
18206 }
18207
18208 if (!zoomDefd && !panDefd) {
18209 return this;
18210 }
18211
18212 if (zoomDefd) {
18213 var z = opts.zoom;
18214
18215 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18216 zoomFailed = true;
18217 } else {
18218 _p.zoom = z;
18219 events.push('zoom');
18220 }
18221 }
18222
18223 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18224 var p = opts.pan;
18225
18226 if (number(p.x)) {
18227 _p.pan.x = p.x;
18228 panFailed = false;
18229 }
18230
18231 if (number(p.y)) {
18232 _p.pan.y = p.y;
18233 panFailed = false;
18234 }
18235
18236 if (!panFailed) {
18237 events.push('pan');
18238 }
18239 }
18240
18241 if (events.length > 0) {
18242 events.push('viewport');
18243 this.emit(events.join(' '));
18244 this.notify('viewport');
18245 }
18246
18247 return this; // chaining
18248 },
18249 center: function center(elements) {
18250 var pan = this.getCenterPan(elements);
18251
18252 if (pan) {
18253 this._private.pan = pan;
18254 this.emit('pan viewport');
18255 this.notify('viewport');
18256 }
18257
18258 return this; // chaining
18259 },
18260 getCenterPan: function getCenterPan(elements, zoom) {
18261 if (!this._private.panningEnabled) {
18262 return;
18263 }
18264
18265 if (string(elements)) {
18266 var selector = elements;
18267 elements = this.mutableElements().filter(selector);
18268 } else if (!elementOrCollection(elements)) {
18269 elements = this.mutableElements();
18270 }
18271
18272 if (elements.length === 0) {
18273 return;
18274 } // can't centre pan to nothing
18275
18276
18277 var bb = elements.boundingBox();
18278 var w = this.width();
18279 var h = this.height();
18280 zoom = zoom === undefined ? this._private.zoom : zoom;
18281 var pan = {
18282 // middle
18283 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18284 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18285 };
18286 return pan;
18287 },
18288 reset: function reset() {
18289 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18290 return this;
18291 }
18292
18293 this.viewport({
18294 pan: {
18295 x: 0,
18296 y: 0
18297 },
18298 zoom: 1
18299 });
18300 return this; // chaining
18301 },
18302 invalidateSize: function invalidateSize() {
18303 this._private.sizeCache = null;
18304 },
18305 size: function size() {
18306 var _p = this._private;
18307 var container = _p.container;
18308 return _p.sizeCache = _p.sizeCache || (container ? function () {
18309 var style = window$1.getComputedStyle(container);
18310
18311 var val = function val(name) {
18312 return parseFloat(style.getPropertyValue(name));
18313 };
18314
18315 return {
18316 width: container.clientWidth - val('padding-left') - val('padding-right'),
18317 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18318 };
18319 }() : {
18320 // fallback if no container (not 0 b/c can be used for dividing etc)
18321 width: 1,
18322 height: 1
18323 });
18324 },
18325 width: function width() {
18326 return this.size().width;
18327 },
18328 height: function height() {
18329 return this.size().height;
18330 },
18331 extent: function extent() {
18332 var pan = this._private.pan;
18333 var zoom = this._private.zoom;
18334 var rb = this.renderedExtent();
18335 var b = {
18336 x1: (rb.x1 - pan.x) / zoom,
18337 x2: (rb.x2 - pan.x) / zoom,
18338 y1: (rb.y1 - pan.y) / zoom,
18339 y2: (rb.y2 - pan.y) / zoom
18340 };
18341 b.w = b.x2 - b.x1;
18342 b.h = b.y2 - b.y1;
18343 return b;
18344 },
18345 renderedExtent: function renderedExtent() {
18346 var width = this.width();
18347 var height = this.height();
18348 return {
18349 x1: 0,
18350 y1: 0,
18351 x2: width,
18352 y2: height,
18353 w: width,
18354 h: height
18355 };
18356 }
18357}; // aliases
18358
18359corefn$8.centre = corefn$8.center; // backwards compatibility
18360
18361corefn$8.autolockNodes = corefn$8.autolock;
18362corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
18363
18364var fn$6 = {
18365 data: define$3.data({
18366 field: 'data',
18367 bindingEvent: 'data',
18368 allowBinding: true,
18369 allowSetting: true,
18370 settingEvent: 'data',
18371 settingTriggersEvent: true,
18372 triggerFnName: 'trigger',
18373 allowGetting: true
18374 }),
18375 removeData: define$3.removeData({
18376 field: 'data',
18377 event: 'data',
18378 triggerFnName: 'trigger',
18379 triggerEvent: true
18380 }),
18381 scratch: define$3.data({
18382 field: 'scratch',
18383 bindingEvent: 'scratch',
18384 allowBinding: true,
18385 allowSetting: true,
18386 settingEvent: 'scratch',
18387 settingTriggersEvent: true,
18388 triggerFnName: 'trigger',
18389 allowGetting: true
18390 }),
18391 removeScratch: define$3.removeData({
18392 field: 'scratch',
18393 event: 'scratch',
18394 triggerFnName: 'trigger',
18395 triggerEvent: true
18396 })
18397}; // aliases
18398
18399fn$6.attr = fn$6.data;
18400fn$6.removeAttr = fn$6.removeData;
18401
18402var Core = function Core(opts) {
18403 var cy = this;
18404 opts = extend({}, opts);
18405 var container = opts.container; // allow for passing a wrapped jquery object
18406 // e.g. cytoscape({ container: $('#cy') })
18407
18408 if (container && !htmlElement(container) && htmlElement(container[0])) {
18409 container = container[0];
18410 }
18411
18412 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18413
18414 reg = reg || {};
18415
18416 if (reg && reg.cy) {
18417 reg.cy.destroy();
18418 reg = {}; // old instance => replace reg completely
18419 }
18420
18421 var readies = reg.readies = reg.readies || [];
18422
18423 if (container) {
18424 container._cyreg = reg;
18425 } // make sure container assoc'd reg points to this cy
18426
18427
18428 reg.cy = cy;
18429 var head = window$1 !== undefined && container !== undefined && !opts.headless;
18430 var options = opts;
18431 options.layout = extend({
18432 name: head ? 'grid' : 'null'
18433 }, options.layout);
18434 options.renderer = extend({
18435 name: head ? 'canvas' : 'null'
18436 }, options.renderer);
18437
18438 var defVal = function defVal(def, val, altVal) {
18439 if (val !== undefined) {
18440 return val;
18441 } else if (altVal !== undefined) {
18442 return altVal;
18443 } else {
18444 return def;
18445 }
18446 };
18447
18448 var _p = this._private = {
18449 container: container,
18450 // html dom ele container
18451 ready: false,
18452 // whether ready has been triggered
18453 options: options,
18454 // cached options
18455 elements: new Collection(this),
18456 // elements in the graph
18457 listeners: [],
18458 // list of listeners
18459 aniEles: new Collection(this),
18460 // elements being animated
18461 data: {},
18462 // data for the core
18463 scratch: {},
18464 // scratch object for core
18465 layout: null,
18466 renderer: null,
18467 destroyed: false,
18468 // whether destroy was called
18469 notificationsEnabled: true,
18470 // whether notifications are sent to the renderer
18471 minZoom: 1e-50,
18472 maxZoom: 1e50,
18473 zoomingEnabled: defVal(true, options.zoomingEnabled),
18474 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18475 panningEnabled: defVal(true, options.panningEnabled),
18476 userPanningEnabled: defVal(true, options.userPanningEnabled),
18477 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18478 autolock: defVal(false, options.autolock, options.autolockNodes),
18479 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18480 autounselectify: defVal(false, options.autounselectify),
18481 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18482 zoom: number(options.zoom) ? options.zoom : 1,
18483 pan: {
18484 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
18485 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
18486 },
18487 animation: {
18488 // object for currently-running animations
18489 current: [],
18490 queue: []
18491 },
18492 hasCompoundNodes: false
18493 };
18494
18495 this.createEmitter(); // set selection type
18496
18497 this.selectionType(options.selectionType); // init zoom bounds
18498
18499 this.zoomRange({
18500 min: options.minZoom,
18501 max: options.maxZoom
18502 });
18503
18504 var loadExtData = function loadExtData(extData, next) {
18505 var anyIsPromise = extData.some(promise);
18506
18507 if (anyIsPromise) {
18508 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18509 } else {
18510 next(extData); // exec synchronously for convenience
18511 }
18512 }; // start with the default stylesheet so we have something before loading an external stylesheet
18513
18514
18515 if (_p.styleEnabled) {
18516 cy.setStyle([]);
18517 } // create the renderer
18518
18519
18520 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18521
18522 cy.initRenderer(rendererOptions);
18523
18524 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18525 cy.notifications(false); // remove old elements
18526
18527 var oldEles = cy.mutableElements();
18528
18529 if (oldEles.length > 0) {
18530 oldEles.remove();
18531 }
18532
18533 if (elements != null) {
18534 if (plainObject(elements) || array(elements)) {
18535 cy.add(elements);
18536 }
18537 }
18538
18539 cy.one('layoutready', function (e) {
18540 cy.notifications(true);
18541 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18542
18543 cy.one('load', onload);
18544 cy.emitAndNotify('load');
18545 }).one('layoutstop', function () {
18546 cy.one('done', ondone);
18547 cy.emit('done');
18548 });
18549 var layoutOpts = extend({}, cy._private.options.layout);
18550 layoutOpts.eles = cy.elements();
18551 cy.layout(layoutOpts).run();
18552 };
18553
18554 loadExtData([options.style, options.elements], function (thens) {
18555 var initStyle = thens[0];
18556 var initEles = thens[1]; // init style
18557
18558 if (_p.styleEnabled) {
18559 cy.style().append(initStyle);
18560 } // initial load
18561
18562
18563 setElesAndLayout(initEles, function () {
18564 // onready
18565 cy.startAnimationLoop();
18566 _p.ready = true; // if a ready callback is specified as an option, the bind it
18567
18568 if (fn(options.ready)) {
18569 cy.on('ready', options.ready);
18570 } // bind all the ready handlers registered before creating this instance
18571
18572
18573 for (var i = 0; i < readies.length; i++) {
18574 var fn$1 = readies[i];
18575 cy.on('ready', fn$1);
18576 }
18577
18578 if (reg) {
18579 reg.readies = [];
18580 } // 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
18581
18582
18583 cy.emit('ready');
18584 }, options.done);
18585 });
18586};
18587
18588var corefn$9 = Core.prototype; // short alias
18589
18590extend(corefn$9, {
18591 instanceString: function instanceString() {
18592 return 'core';
18593 },
18594 isReady: function isReady() {
18595 return this._private.ready;
18596 },
18597 destroyed: function destroyed() {
18598 return this._private.destroyed;
18599 },
18600 ready: function ready(fn) {
18601 if (this.isReady()) {
18602 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18603 } else {
18604 this.on('ready', fn);
18605 }
18606
18607 return this;
18608 },
18609 destroy: function destroy() {
18610 var cy = this;
18611 if (cy.destroyed()) return;
18612 cy.stopAnimationLoop();
18613 cy.destroyRenderer();
18614 this.emit('destroy');
18615 cy._private.destroyed = true;
18616 return cy;
18617 },
18618 hasElementWithId: function hasElementWithId(id) {
18619 return this._private.elements.hasElementWithId(id);
18620 },
18621 getElementById: function getElementById(id) {
18622 return this._private.elements.getElementById(id);
18623 },
18624 hasCompoundNodes: function hasCompoundNodes() {
18625 return this._private.hasCompoundNodes;
18626 },
18627 headless: function headless() {
18628 return this._private.renderer.isHeadless();
18629 },
18630 styleEnabled: function styleEnabled() {
18631 return this._private.styleEnabled;
18632 },
18633 addToPool: function addToPool(eles) {
18634 this._private.elements.merge(eles);
18635
18636 return this; // chaining
18637 },
18638 removeFromPool: function removeFromPool(eles) {
18639 this._private.elements.unmerge(eles);
18640
18641 return this;
18642 },
18643 container: function container() {
18644 return this._private.container || null;
18645 },
18646 mount: function mount(container) {
18647 if (container == null) {
18648 return;
18649 }
18650
18651 var cy = this;
18652 var _p = cy._private;
18653 var options = _p.options;
18654
18655 if (!htmlElement(container) && htmlElement(container[0])) {
18656 container = container[0];
18657 }
18658
18659 cy.stopAnimationLoop();
18660 cy.destroyRenderer();
18661 _p.container = container;
18662 _p.styleEnabled = true;
18663 cy.invalidateSize();
18664 cy.initRenderer(extend({}, options, options.renderer, {
18665 // allow custom renderer name to be re-used, otherwise use canvas
18666 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
18667 }));
18668 cy.startAnimationLoop();
18669 cy.style(options.style);
18670 cy.emit('mount');
18671 return cy;
18672 },
18673 unmount: function unmount() {
18674 var cy = this;
18675 cy.stopAnimationLoop();
18676 cy.destroyRenderer();
18677 cy.initRenderer({
18678 name: 'null'
18679 });
18680 cy.emit('unmount');
18681 return cy;
18682 },
18683 options: function options() {
18684 return copy(this._private.options);
18685 },
18686 json: function json(obj) {
18687 var cy = this;
18688 var _p = cy._private;
18689 var eles = cy.mutableElements();
18690
18691 var getFreshRef = function getFreshRef(ele) {
18692 return cy.getElementById(ele.id());
18693 };
18694
18695 if (plainObject(obj)) {
18696 // set
18697 cy.startBatch();
18698
18699 if (obj.elements) {
18700 var idInJson = {};
18701
18702 var updateEles = function updateEles(jsons, gr) {
18703 var toAdd = [];
18704 var toMod = [];
18705
18706 for (var i = 0; i < jsons.length; i++) {
18707 var json = jsons[i];
18708 var id = '' + json.data.id; // id must be string
18709
18710 var ele = cy.getElementById(id);
18711 idInJson[id] = true;
18712
18713 if (ele.length !== 0) {
18714 // existing element should be updated
18715 toMod.push({
18716 ele: ele,
18717 json: json
18718 });
18719 } else {
18720 // otherwise should be added
18721 if (gr) {
18722 json.group = gr;
18723 toAdd.push(json);
18724 } else {
18725 toAdd.push(json);
18726 }
18727 }
18728 }
18729
18730 cy.add(toAdd);
18731
18732 for (var _i = 0; _i < toMod.length; _i++) {
18733 var _toMod$_i = toMod[_i],
18734 _ele = _toMod$_i.ele,
18735 _json = _toMod$_i.json;
18736
18737 _ele.json(_json);
18738 }
18739 };
18740
18741 if (array(obj.elements)) {
18742 // elements: []
18743 updateEles(obj.elements);
18744 } else {
18745 // elements: { nodes: [], edges: [] }
18746 var grs = ['nodes', 'edges'];
18747
18748 for (var i = 0; i < grs.length; i++) {
18749 var gr = grs[i];
18750 var elements = obj.elements[gr];
18751
18752 if (array(elements)) {
18753 updateEles(elements, gr);
18754 }
18755 }
18756 }
18757
18758 var parentsToRemove = cy.collection();
18759 eles.filter(function (ele) {
18760 return !idInJson[ele.id()];
18761 }).forEach(function (ele) {
18762 if (ele.isParent()) {
18763 parentsToRemove.merge(ele);
18764 } else {
18765 ele.remove();
18766 }
18767 }); // so that children are not removed w/parent
18768
18769 parentsToRemove.forEach(function (ele) {
18770 return ele.children().move({
18771 parent: null
18772 });
18773 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
18774
18775 parentsToRemove.forEach(function (ele) {
18776 return getFreshRef(ele).remove();
18777 });
18778 }
18779
18780 if (obj.style) {
18781 cy.style(obj.style);
18782 }
18783
18784 if (obj.zoom != null && obj.zoom !== _p.zoom) {
18785 cy.zoom(obj.zoom);
18786 }
18787
18788 if (obj.pan) {
18789 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
18790 cy.pan(obj.pan);
18791 }
18792 }
18793
18794 if (obj.data) {
18795 cy.data(obj.data);
18796 }
18797
18798 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
18799
18800 for (var _i2 = 0; _i2 < fields.length; _i2++) {
18801 var f = fields[_i2];
18802
18803 if (obj[f] != null) {
18804 cy[f](obj[f]);
18805 }
18806 }
18807
18808 cy.endBatch();
18809 return this; // chaining
18810 } else {
18811 // get
18812 var flat = !!obj;
18813 var json = {};
18814
18815 if (flat) {
18816 json.elements = this.elements().map(function (ele) {
18817 return ele.json();
18818 });
18819 } else {
18820 json.elements = {};
18821 eles.forEach(function (ele) {
18822 var group = ele.group();
18823
18824 if (!json.elements[group]) {
18825 json.elements[group] = [];
18826 }
18827
18828 json.elements[group].push(ele.json());
18829 });
18830 }
18831
18832 if (this._private.styleEnabled) {
18833 json.style = cy.style().json();
18834 }
18835
18836 json.data = copy(cy.data());
18837 var options = _p.options;
18838 json.zoomingEnabled = _p.zoomingEnabled;
18839 json.userZoomingEnabled = _p.userZoomingEnabled;
18840 json.zoom = _p.zoom;
18841 json.minZoom = _p.minZoom;
18842 json.maxZoom = _p.maxZoom;
18843 json.panningEnabled = _p.panningEnabled;
18844 json.userPanningEnabled = _p.userPanningEnabled;
18845 json.pan = copy(_p.pan);
18846 json.boxSelectionEnabled = _p.boxSelectionEnabled;
18847 json.renderer = copy(options.renderer);
18848 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
18849 json.textureOnViewport = options.textureOnViewport;
18850 json.wheelSensitivity = options.wheelSensitivity;
18851 json.motionBlur = options.motionBlur;
18852 return json;
18853 }
18854 }
18855});
18856corefn$9.$id = corefn$9.getElementById;
18857[corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
18858 extend(corefn$9, props);
18859});
18860
18861/* eslint-disable no-unused-vars */
18862
18863var defaults$9 = {
18864 fit: true,
18865 // whether to fit the viewport to the graph
18866 directed: false,
18867 // whether the tree is directed downwards (or edges can point in any direction if false)
18868 padding: 30,
18869 // padding on fit
18870 circle: false,
18871 // put depths in concentric circles if true, put depths top down if false
18872 grid: false,
18873 // whether to create an even grid into which the DAG is placed (circle:false only)
18874 spacingFactor: 1.75,
18875 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
18876 boundingBox: undefined,
18877 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
18878 avoidOverlap: true,
18879 // prevents node overlap, may overflow boundingBox if not enough space
18880 nodeDimensionsIncludeLabels: false,
18881 // Excludes the label when calculating node bounding boxes for the layout algorithm
18882 roots: undefined,
18883 // the roots of the trees
18884 maximal: false,
18885 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
18886 animate: false,
18887 // whether to transition the node positions
18888 animationDuration: 500,
18889 // duration of animation in ms if enabled
18890 animationEasing: undefined,
18891 // easing of animation if enabled,
18892 animateFilter: function animateFilter(node, i) {
18893 return true;
18894 },
18895 // 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
18896 ready: undefined,
18897 // callback on layoutready
18898 stop: undefined,
18899 // callback on layoutstop
18900 transform: function transform(node, position) {
18901 return position;
18902 } // transform a given node position. Useful for changing flow direction in discrete layouts
18903
18904};
18905/* eslint-enable */
18906
18907var getInfo = function getInfo(ele) {
18908 return ele.scratch('breadthfirst');
18909};
18910
18911var setInfo = function setInfo(ele, obj) {
18912 return ele.scratch('breadthfirst', obj);
18913};
18914
18915function BreadthFirstLayout(options) {
18916 this.options = extend({}, defaults$9, options);
18917}
18918
18919BreadthFirstLayout.prototype.run = function () {
18920 var params = this.options;
18921 var options = params;
18922 var cy = params.cy;
18923 var eles = options.eles;
18924 var nodes = eles.nodes().filter(function (n) {
18925 return !n.isParent();
18926 });
18927 var graph = eles;
18928 var directed = options.directed;
18929 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
18930
18931 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
18932 x1: 0,
18933 y1: 0,
18934 w: cy.width(),
18935 h: cy.height()
18936 });
18937 var roots;
18938
18939 if (elementOrCollection(options.roots)) {
18940 roots = options.roots;
18941 } else if (array(options.roots)) {
18942 var rootsArray = [];
18943
18944 for (var i = 0; i < options.roots.length; i++) {
18945 var id = options.roots[i];
18946 var ele = cy.getElementById(id);
18947 rootsArray.push(ele);
18948 }
18949
18950 roots = cy.collection(rootsArray);
18951 } else if (string(options.roots)) {
18952 roots = cy.$(options.roots);
18953 } else {
18954 if (directed) {
18955 roots = nodes.roots();
18956 } else {
18957 var components = eles.components();
18958 roots = cy.collection();
18959
18960 var _loop = function _loop(_i) {
18961 var comp = components[_i];
18962 var maxDegree = comp.maxDegree(false);
18963 var compRoots = comp.filter(function (ele) {
18964 return ele.degree(false) === maxDegree;
18965 });
18966 roots = roots.add(compRoots);
18967 };
18968
18969 for (var _i = 0; _i < components.length; _i++) {
18970 _loop(_i);
18971 }
18972 }
18973 }
18974
18975 var depths = [];
18976 var foundByBfs = {};
18977
18978 var addToDepth = function addToDepth(ele, d) {
18979 if (depths[d] == null) {
18980 depths[d] = [];
18981 }
18982
18983 var i = depths[d].length;
18984 depths[d].push(ele);
18985 setInfo(ele, {
18986 index: i,
18987 depth: d
18988 });
18989 };
18990
18991 var changeDepth = function changeDepth(ele, newDepth) {
18992 var _getInfo = getInfo(ele),
18993 depth = _getInfo.depth,
18994 index = _getInfo.index;
18995
18996 depths[depth][index] = null;
18997 addToDepth(ele, newDepth);
18998 }; // find the depths of the nodes
18999
19000
19001 graph.bfs({
19002 roots: roots,
19003 directed: options.directed,
19004 visit: function visit(node, edge, pNode, i, depth) {
19005 var ele = node[0];
19006 var id = ele.id();
19007 addToDepth(ele, depth);
19008 foundByBfs[id] = true;
19009 }
19010 }); // check for nodes not found by bfs
19011
19012 var orphanNodes = [];
19013
19014 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19015 var _ele = nodes[_i2];
19016
19017 if (foundByBfs[_ele.id()]) {
19018 continue;
19019 } else {
19020 orphanNodes.push(_ele);
19021 }
19022 } // assign the nodes a depth and index
19023
19024
19025 var assignDepthsAt = function assignDepthsAt(i) {
19026 var eles = depths[i];
19027
19028 for (var j = 0; j < eles.length; j++) {
19029 var _ele2 = eles[j];
19030
19031 if (_ele2 == null) {
19032 eles.splice(j, 1);
19033 j--;
19034 continue;
19035 }
19036
19037 setInfo(_ele2, {
19038 depth: i,
19039 index: j
19040 });
19041 }
19042 };
19043
19044 var assignDepths = function assignDepths() {
19045 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19046 assignDepthsAt(_i3);
19047 }
19048 };
19049
19050 var adjustMaximally = function adjustMaximally(ele, shifted) {
19051 var eInfo = getInfo(ele);
19052 var incomers = ele.incomers().filter(function (el) {
19053 return el.isNode() && eles.has(el);
19054 });
19055 var maxDepth = -1;
19056 var id = ele.id();
19057
19058 for (var k = 0; k < incomers.length; k++) {
19059 var incmr = incomers[k];
19060 var iInfo = getInfo(incmr);
19061 maxDepth = Math.max(maxDepth, iInfo.depth);
19062 }
19063
19064 if (eInfo.depth <= maxDepth) {
19065 if (shifted[id]) {
19066 return null;
19067 }
19068
19069 changeDepth(ele, maxDepth + 1);
19070 shifted[id] = true;
19071 return true;
19072 }
19073
19074 return false;
19075 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19076
19077
19078 if (directed && maximal) {
19079 var Q = [];
19080 var shifted = {};
19081
19082 var enqueue = function enqueue(n) {
19083 return Q.push(n);
19084 };
19085
19086 var dequeue = function dequeue() {
19087 return Q.shift();
19088 };
19089
19090 nodes.forEach(function (n) {
19091 return Q.push(n);
19092 });
19093
19094 while (Q.length > 0) {
19095 var _ele3 = dequeue();
19096
19097 var didShift = adjustMaximally(_ele3, shifted);
19098
19099 if (didShift) {
19100 _ele3.outgoers().filter(function (el) {
19101 return el.isNode() && eles.has(el);
19102 }).forEach(enqueue);
19103 } else if (didShift === null) {
19104 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19105 break; // exit on failure
19106 }
19107 }
19108 }
19109
19110 assignDepths(); // clear holes
19111 // find min distance we need to leave between nodes
19112
19113 var minDistance = 0;
19114
19115 if (options.avoidOverlap) {
19116 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19117 var n = nodes[_i4];
19118 var nbb = n.layoutDimensions(options);
19119 var w = nbb.w;
19120 var h = nbb.h;
19121 minDistance = Math.max(minDistance, w, h);
19122 }
19123 } // get the weighted percent for an element based on its connectivity to other levels
19124
19125
19126 var cachedWeightedPercent = {};
19127
19128 var getWeightedPercent = function getWeightedPercent(ele) {
19129 if (cachedWeightedPercent[ele.id()]) {
19130 return cachedWeightedPercent[ele.id()];
19131 }
19132
19133 var eleDepth = getInfo(ele).depth;
19134 var neighbors = ele.neighborhood();
19135 var percent = 0;
19136 var samples = 0;
19137
19138 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19139 var neighbor = neighbors[_i5];
19140
19141 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19142 continue;
19143 }
19144
19145 var bf = getInfo(neighbor);
19146 var index = bf.index;
19147 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19148
19149 if (index == null || depth == null) {
19150 continue;
19151 }
19152
19153 var nDepth = depths[depth].length;
19154
19155 if (depth < eleDepth) {
19156 // only get influenced by elements above
19157 percent += index / nDepth;
19158 samples++;
19159 }
19160 }
19161
19162 samples = Math.max(1, samples);
19163 percent = percent / samples;
19164
19165 if (samples === 0) {
19166 // put lone nodes at the start
19167 percent = 0;
19168 }
19169
19170 cachedWeightedPercent[ele.id()] = percent;
19171 return percent;
19172 }; // rearrange the indices in each depth level based on connectivity
19173
19174
19175 var sortFn = function sortFn(a, b) {
19176 var apct = getWeightedPercent(a);
19177 var bpct = getWeightedPercent(b);
19178 var diff = apct - bpct;
19179
19180 if (diff === 0) {
19181 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19182 } else {
19183 return diff;
19184 }
19185 }; // sort each level to make connected nodes closer
19186
19187
19188 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19189 depths[_i6].sort(sortFn);
19190
19191 assignDepthsAt(_i6);
19192 } // assign orphan nodes to a new top-level depth
19193
19194
19195 var orphanDepth = [];
19196
19197 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19198 orphanDepth.push(orphanNodes[_i7]);
19199 }
19200
19201 depths.unshift(orphanDepth);
19202 assignDepths();
19203 var biggestDepthSize = 0;
19204
19205 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19206 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19207 }
19208
19209 var center = {
19210 x: bb.x1 + bb.w / 2,
19211 y: bb.x1 + bb.h / 2
19212 };
19213 var maxDepthSize = depths.reduce(function (max, eles) {
19214 return Math.max(max, eles.length);
19215 }, 0);
19216
19217 var getPosition = function getPosition(ele) {
19218 var _getInfo2 = getInfo(ele),
19219 depth = _getInfo2.depth,
19220 index = _getInfo2.index;
19221
19222 var depthSize = depths[depth].length;
19223 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19224 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19225 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19226 radiusStepSize = Math.max(radiusStepSize, minDistance);
19227
19228 if (!options.circle) {
19229 var epos = {
19230 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19231 y: (depth + 1) * distanceY
19232 };
19233 return epos;
19234 } else {
19235 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19236 var theta = 2 * Math.PI / depths[depth].length * index;
19237
19238 if (depth === 0 && depths[0].length === 1) {
19239 radius = 1;
19240 }
19241
19242 return {
19243 x: center.x + radius * Math.cos(theta),
19244 y: center.y + radius * Math.sin(theta)
19245 };
19246 }
19247 };
19248
19249 nodes.layoutPositions(this, options, getPosition);
19250 return this; // chaining
19251};
19252
19253var defaults$a = {
19254 fit: true,
19255 // whether to fit the viewport to the graph
19256 padding: 30,
19257 // the padding on fit
19258 boundingBox: undefined,
19259 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19260 avoidOverlap: true,
19261 // prevents node overlap, may overflow boundingBox and radius if not enough space
19262 nodeDimensionsIncludeLabels: false,
19263 // Excludes the label when calculating node bounding boxes for the layout algorithm
19264 spacingFactor: undefined,
19265 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19266 radius: undefined,
19267 // the radius of the circle
19268 startAngle: 3 / 2 * Math.PI,
19269 // where nodes start in radians
19270 sweep: undefined,
19271 // how many radians should be between the first and last node (defaults to full circle)
19272 clockwise: true,
19273 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19274 sort: undefined,
19275 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19276 animate: false,
19277 // whether to transition the node positions
19278 animationDuration: 500,
19279 // duration of animation in ms if enabled
19280 animationEasing: undefined,
19281 // easing of animation if enabled
19282 animateFilter: function animateFilter(node, i) {
19283 return true;
19284 },
19285 // 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
19286 ready: undefined,
19287 // callback on layoutready
19288 stop: undefined,
19289 // callback on layoutstop
19290 transform: function transform(node, position) {
19291 return position;
19292 } // transform a given node position. Useful for changing flow direction in discrete layouts
19293
19294};
19295
19296function CircleLayout(options) {
19297 this.options = extend({}, defaults$a, options);
19298}
19299
19300CircleLayout.prototype.run = function () {
19301 var params = this.options;
19302 var options = params;
19303 var cy = params.cy;
19304 var eles = options.eles;
19305 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19306 var nodes = eles.nodes().not(':parent');
19307
19308 if (options.sort) {
19309 nodes = nodes.sort(options.sort);
19310 }
19311
19312 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19313 x1: 0,
19314 y1: 0,
19315 w: cy.width(),
19316 h: cy.height()
19317 });
19318 var center = {
19319 x: bb.x1 + bb.w / 2,
19320 y: bb.y1 + bb.h / 2
19321 };
19322 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19323 var dTheta = sweep / Math.max(1, nodes.length - 1);
19324 var r;
19325 var minDistance = 0;
19326
19327 for (var i = 0; i < nodes.length; i++) {
19328 var n = nodes[i];
19329 var nbb = n.layoutDimensions(options);
19330 var w = nbb.w;
19331 var h = nbb.h;
19332 minDistance = Math.max(minDistance, w, h);
19333 }
19334
19335 if (number(options.radius)) {
19336 r = options.radius;
19337 } else if (nodes.length <= 1) {
19338 r = 0;
19339 } else {
19340 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19341 } // calculate the radius
19342
19343
19344 if (nodes.length > 1 && options.avoidOverlap) {
19345 // but only if more than one node (can't overlap)
19346 minDistance *= 1.75; // just to have some nice spacing
19347
19348 var dcos = Math.cos(dTheta) - Math.cos(0);
19349 var dsin = Math.sin(dTheta) - Math.sin(0);
19350 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19351
19352 r = Math.max(rMin, r);
19353 }
19354
19355 var getPos = function getPos(ele, i) {
19356 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19357 var rx = r * Math.cos(theta);
19358 var ry = r * Math.sin(theta);
19359 var pos = {
19360 x: center.x + rx,
19361 y: center.y + ry
19362 };
19363 return pos;
19364 };
19365
19366 nodes.layoutPositions(this, options, getPos);
19367 return this; // chaining
19368};
19369
19370var defaults$b = {
19371 fit: true,
19372 // whether to fit the viewport to the graph
19373 padding: 30,
19374 // the padding on fit
19375 startAngle: 3 / 2 * Math.PI,
19376 // where nodes start in radians
19377 sweep: undefined,
19378 // how many radians should be between the first and last node (defaults to full circle)
19379 clockwise: true,
19380 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19381 equidistant: false,
19382 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19383 minNodeSpacing: 10,
19384 // min spacing between outside of nodes (used for radius adjustment)
19385 boundingBox: undefined,
19386 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19387 avoidOverlap: true,
19388 // prevents node overlap, may overflow boundingBox if not enough space
19389 nodeDimensionsIncludeLabels: false,
19390 // Excludes the label when calculating node bounding boxes for the layout algorithm
19391 height: undefined,
19392 // height of layout area (overrides container height)
19393 width: undefined,
19394 // width of layout area (overrides container width)
19395 spacingFactor: undefined,
19396 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19397 concentric: function concentric(node) {
19398 // returns numeric value for each node, placing higher nodes in levels towards the centre
19399 return node.degree();
19400 },
19401 levelWidth: function levelWidth(nodes) {
19402 // the variation of concentric values in each level
19403 return nodes.maxDegree() / 4;
19404 },
19405 animate: false,
19406 // whether to transition the node positions
19407 animationDuration: 500,
19408 // duration of animation in ms if enabled
19409 animationEasing: undefined,
19410 // easing of animation if enabled
19411 animateFilter: function animateFilter(node, i) {
19412 return true;
19413 },
19414 // 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
19415 ready: undefined,
19416 // callback on layoutready
19417 stop: undefined,
19418 // callback on layoutstop
19419 transform: function transform(node, position) {
19420 return position;
19421 } // transform a given node position. Useful for changing flow direction in discrete layouts
19422
19423};
19424
19425function ConcentricLayout(options) {
19426 this.options = extend({}, defaults$b, options);
19427}
19428
19429ConcentricLayout.prototype.run = function () {
19430 var params = this.options;
19431 var options = params;
19432 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19433 var cy = params.cy;
19434 var eles = options.eles;
19435 var nodes = eles.nodes().not(':parent');
19436 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19437 x1: 0,
19438 y1: 0,
19439 w: cy.width(),
19440 h: cy.height()
19441 });
19442 var center = {
19443 x: bb.x1 + bb.w / 2,
19444 y: bb.y1 + bb.h / 2
19445 };
19446 var nodeValues = []; // { node, value }
19447
19448 var maxNodeSize = 0;
19449
19450 for (var i = 0; i < nodes.length; i++) {
19451 var node = nodes[i];
19452 var value = void 0; // calculate the node value
19453
19454 value = options.concentric(node);
19455 nodeValues.push({
19456 value: value,
19457 node: node
19458 }); // for style mapping
19459
19460 node._private.scratch.concentric = value;
19461 } // in case we used the `concentric` in style
19462
19463
19464 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
19465
19466 for (var _i = 0; _i < nodes.length; _i++) {
19467 var _node = nodes[_i];
19468
19469 var nbb = _node.layoutDimensions(options);
19470
19471 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19472 } // sort node values in descreasing order
19473
19474
19475 nodeValues.sort(function (a, b) {
19476 return b.value - a.value;
19477 });
19478 var levelWidth = options.levelWidth(nodes); // put the values into levels
19479
19480 var levels = [[]];
19481 var currentLevel = levels[0];
19482
19483 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19484 var val = nodeValues[_i2];
19485
19486 if (currentLevel.length > 0) {
19487 var diff = Math.abs(currentLevel[0].value - val.value);
19488
19489 if (diff >= levelWidth) {
19490 currentLevel = [];
19491 levels.push(currentLevel);
19492 }
19493 }
19494
19495 currentLevel.push(val);
19496 } // create positions from levels
19497
19498
19499 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19500
19501 if (!options.avoidOverlap) {
19502 // then strictly constrain to bb
19503 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19504 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19505 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19506 minDist = Math.min(minDist, rStep);
19507 } // find the metrics for each level
19508
19509
19510 var r = 0;
19511
19512 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19513 var level = levels[_i3];
19514 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19515 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
19516
19517 if (level.length > 1 && options.avoidOverlap) {
19518 // but only if more than one node (can't overlap)
19519 var dcos = Math.cos(dTheta) - Math.cos(0);
19520 var dsin = Math.sin(dTheta) - Math.sin(0);
19521 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19522
19523 r = Math.max(rMin, r);
19524 }
19525
19526 level.r = r;
19527 r += minDist;
19528 }
19529
19530 if (options.equidistant) {
19531 var rDeltaMax = 0;
19532 var _r = 0;
19533
19534 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19535 var _level = levels[_i4];
19536 var rDelta = _level.r - _r;
19537 rDeltaMax = Math.max(rDeltaMax, rDelta);
19538 }
19539
19540 _r = 0;
19541
19542 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19543 var _level2 = levels[_i5];
19544
19545 if (_i5 === 0) {
19546 _r = _level2.r;
19547 }
19548
19549 _level2.r = _r;
19550 _r += rDeltaMax;
19551 }
19552 } // calculate the node positions
19553
19554
19555 var pos = {}; // id => position
19556
19557 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19558 var _level3 = levels[_i6];
19559 var _dTheta = _level3.dTheta;
19560 var _r2 = _level3.r;
19561
19562 for (var j = 0; j < _level3.length; j++) {
19563 var _val = _level3[j];
19564 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19565 var p = {
19566 x: center.x + _r2 * Math.cos(theta),
19567 y: center.y + _r2 * Math.sin(theta)
19568 };
19569 pos[_val.node.id()] = p;
19570 }
19571 } // position the nodes
19572
19573
19574 nodes.layoutPositions(this, options, function (ele) {
19575 var id = ele.id();
19576 return pos[id];
19577 });
19578 return this; // chaining
19579};
19580
19581/*
19582The CoSE layout was written by Gerardo Huck.
19583https://www.linkedin.com/in/gerardohuck/
19584
19585Based on the following article:
19586http://dl.acm.org/citation.cfm?id=1498047
19587
19588Modifications tracked on Github.
19589*/
19590var DEBUG;
19591/**
19592 * @brief : default layout options
19593 */
19594
19595var defaults$c = {
19596 // Called on `layoutready`
19597 ready: function ready() {},
19598 // Called on `layoutstop`
19599 stop: function stop() {},
19600 // Whether to animate while running the layout
19601 // true : Animate continuously as the layout is running
19602 // false : Just show the end result
19603 // 'end' : Animate with the end result, from the initial positions to the end positions
19604 animate: true,
19605 // Easing of the animation for animate:'end'
19606 animationEasing: undefined,
19607 // The duration of the animation for animate:'end'
19608 animationDuration: undefined,
19609 // A function that determines whether the node should be animated
19610 // All nodes animated by default on animate enabled
19611 // Non-animated nodes are positioned immediately when the layout starts
19612 animateFilter: function animateFilter(node, i) {
19613 return true;
19614 },
19615 // The layout animates only after this many milliseconds for animate:true
19616 // (prevents flashing on fast runs)
19617 animationThreshold: 250,
19618 // Number of iterations between consecutive screen positions update
19619 refresh: 20,
19620 // Whether to fit the network view after when done
19621 fit: true,
19622 // Padding on fit
19623 padding: 30,
19624 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19625 boundingBox: undefined,
19626 // Excludes the label when calculating node bounding boxes for the layout algorithm
19627 nodeDimensionsIncludeLabels: false,
19628 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19629 randomize: false,
19630 // Extra spacing between components in non-compound graphs
19631 componentSpacing: 40,
19632 // Node repulsion (non overlapping) multiplier
19633 nodeRepulsion: function nodeRepulsion(node) {
19634 return 2048;
19635 },
19636 // Node repulsion (overlapping) multiplier
19637 nodeOverlap: 4,
19638 // Ideal edge (non nested) length
19639 idealEdgeLength: function idealEdgeLength(edge) {
19640 return 32;
19641 },
19642 // Divisor to compute edge forces
19643 edgeElasticity: function edgeElasticity(edge) {
19644 return 32;
19645 },
19646 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19647 nestingFactor: 1.2,
19648 // Gravity force (constant)
19649 gravity: 1,
19650 // Maximum number of iterations to perform
19651 numIter: 1000,
19652 // Initial temperature (maximum node displacement)
19653 initialTemp: 1000,
19654 // Cooling factor (how the temperature is reduced between consecutive iterations
19655 coolingFactor: 0.99,
19656 // Lower temperature threshold (below this point the layout will end)
19657 minTemp: 1.0
19658};
19659/**
19660 * @brief : constructor
19661 * @arg options : object containing layout options
19662 */
19663
19664function CoseLayout(options) {
19665 this.options = extend({}, defaults$c, options);
19666 this.options.layout = this;
19667}
19668/**
19669 * @brief : runs the layout
19670 */
19671
19672
19673CoseLayout.prototype.run = function () {
19674 var options = this.options;
19675 var cy = options.cy;
19676 var layout = this;
19677 layout.stopped = false;
19678
19679 if (options.animate === true || options.animate === false) {
19680 layout.emit({
19681 type: 'layoutstart',
19682 layout: layout
19683 });
19684 } // Set DEBUG - Global variable
19685
19686
19687 if (true === options.debug) {
19688 DEBUG = true;
19689 } else {
19690 DEBUG = false;
19691 } // Initialize layout info
19692
19693
19694 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
19695
19696 if (DEBUG) {
19697 printLayoutInfo(layoutInfo);
19698 } // If required, randomize node positions
19699
19700
19701 if (options.randomize) {
19702 randomizePositions(layoutInfo);
19703 }
19704
19705 var startTime = performanceNow();
19706
19707 var refresh = function refresh() {
19708 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
19709
19710 if (true === options.fit) {
19711 cy.fit(options.padding);
19712 }
19713 };
19714
19715 var mainLoop = function mainLoop(i) {
19716 if (layout.stopped || i >= options.numIter) {
19717 // logDebug("Layout manually stopped. Stopping computation in step " + i);
19718 return false;
19719 } // Do one step in the phisical simulation
19720
19721
19722 step$1(layoutInfo, options); // Update temperature
19723
19724 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
19725
19726 if (layoutInfo.temperature < options.minTemp) {
19727 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
19728 return false;
19729 }
19730
19731 return true;
19732 };
19733
19734 var done = function done() {
19735 if (options.animate === true || options.animate === false) {
19736 refresh(); // Layout has finished
19737
19738 layout.one('layoutstop', options.stop);
19739 layout.emit({
19740 type: 'layoutstop',
19741 layout: layout
19742 });
19743 } else {
19744 var nodes = options.eles.nodes();
19745 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19746 nodes.layoutPositions(layout, options, getScaledPos);
19747 }
19748 };
19749
19750 var i = 0;
19751 var loopRet = true;
19752
19753 if (options.animate === true) {
19754 var frame = function frame() {
19755 var f = 0;
19756
19757 while (loopRet && f < options.refresh) {
19758 loopRet = mainLoop(i);
19759 i++;
19760 f++;
19761 }
19762
19763 if (!loopRet) {
19764 // it's done
19765 separateComponents(layoutInfo, options);
19766 done();
19767 } else {
19768 var now = performanceNow();
19769
19770 if (now - startTime >= options.animationThreshold) {
19771 refresh();
19772 }
19773
19774 requestAnimationFrame(frame);
19775 }
19776 };
19777
19778 frame();
19779 } else {
19780 while (loopRet) {
19781 loopRet = mainLoop(i);
19782 i++;
19783 }
19784
19785 separateComponents(layoutInfo, options);
19786 done();
19787 }
19788
19789 return this; // chaining
19790};
19791/**
19792 * @brief : called on continuous layouts to stop them before they finish
19793 */
19794
19795
19796CoseLayout.prototype.stop = function () {
19797 this.stopped = true;
19798
19799 if (this.thread) {
19800 this.thread.stop();
19801 }
19802
19803 this.emit('layoutstop');
19804 return this; // chaining
19805};
19806
19807CoseLayout.prototype.destroy = function () {
19808 if (this.thread) {
19809 this.thread.stop();
19810 }
19811
19812 return this; // chaining
19813};
19814/**
19815 * @brief : Creates an object which is contains all the data
19816 * used in the layout process
19817 * @arg cy : cytoscape.js object
19818 * @return : layoutInfo object initialized
19819 */
19820
19821
19822var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
19823 // Shortcut
19824 var edges = options.eles.edges();
19825 var nodes = options.eles.nodes();
19826 var layoutInfo = {
19827 isCompound: cy.hasCompoundNodes(),
19828 layoutNodes: [],
19829 idToIndex: {},
19830 nodeSize: nodes.size(),
19831 graphSet: [],
19832 indexToGraph: [],
19833 layoutEdges: [],
19834 edgeSize: edges.size(),
19835 temperature: options.initialTemp,
19836 clientWidth: cy.width(),
19837 clientHeight: cy.width(),
19838 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
19839 x1: 0,
19840 y1: 0,
19841 w: cy.width(),
19842 h: cy.height()
19843 })
19844 };
19845 var components = options.eles.components();
19846 var id2cmptId = {};
19847
19848 for (var i = 0; i < components.length; i++) {
19849 var component = components[i];
19850
19851 for (var j = 0; j < component.length; j++) {
19852 var node = component[j];
19853 id2cmptId[node.id()] = i;
19854 }
19855 } // Iterate over all nodes, creating layout nodes
19856
19857
19858 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19859 var n = nodes[i];
19860 var nbb = n.layoutDimensions(options);
19861 var tempNode = {};
19862 tempNode.isLocked = n.locked();
19863 tempNode.id = n.data('id');
19864 tempNode.parentId = n.data('parent');
19865 tempNode.cmptId = id2cmptId[n.id()];
19866 tempNode.children = [];
19867 tempNode.positionX = n.position('x');
19868 tempNode.positionY = n.position('y');
19869 tempNode.offsetX = 0;
19870 tempNode.offsetY = 0;
19871 tempNode.height = nbb.w;
19872 tempNode.width = nbb.h;
19873 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
19874 tempNode.minX = tempNode.positionX - tempNode.width / 2;
19875 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
19876 tempNode.minY = tempNode.positionY - tempNode.height / 2;
19877 tempNode.padLeft = parseFloat(n.style('padding'));
19878 tempNode.padRight = parseFloat(n.style('padding'));
19879 tempNode.padTop = parseFloat(n.style('padding'));
19880 tempNode.padBottom = parseFloat(n.style('padding')); // forces
19881
19882 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
19883
19884 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
19885
19886 layoutInfo.idToIndex[tempNode.id] = i;
19887 } // Inline implementation of a queue, used for traversing the graph in BFS order
19888
19889
19890 var queue = [];
19891 var start = 0; // Points to the start the queue
19892
19893 var end = -1; // Points to the end of the queue
19894
19895 var tempGraph = []; // Second pass to add child information and
19896 // initialize queue for hierarchical traversal
19897
19898 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19899 var n = layoutInfo.layoutNodes[i];
19900 var p_id = n.parentId; // Check if node n has a parent node
19901
19902 if (null != p_id) {
19903 // Add node Id to parent's list of children
19904 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
19905 } else {
19906 // If a node doesn't have a parent, then it's in the root graph
19907 queue[++end] = n.id;
19908 tempGraph.push(n.id);
19909 }
19910 } // Add root graph to graphSet
19911
19912
19913 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
19914
19915 while (start <= end) {
19916 // Get the node to visit and remove it from queue
19917 var node_id = queue[start++];
19918 var node_ix = layoutInfo.idToIndex[node_id];
19919 var node = layoutInfo.layoutNodes[node_ix];
19920 var children = node.children;
19921
19922 if (children.length > 0) {
19923 // Add children nodes as a new graph to graph set
19924 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
19925
19926 for (var i = 0; i < children.length; i++) {
19927 queue[++end] = children[i];
19928 }
19929 }
19930 } // Create indexToGraph map
19931
19932
19933 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
19934 var graph = layoutInfo.graphSet[i];
19935
19936 for (var j = 0; j < graph.length; j++) {
19937 var index = layoutInfo.idToIndex[graph[j]];
19938 layoutInfo.indexToGraph[index] = i;
19939 }
19940 } // Iterate over all edges, creating Layout Edges
19941
19942
19943 for (var i = 0; i < layoutInfo.edgeSize; i++) {
19944 var e = edges[i];
19945 var tempEdge = {};
19946 tempEdge.id = e.data('id');
19947 tempEdge.sourceId = e.data('source');
19948 tempEdge.targetId = e.data('target'); // Compute ideal length
19949
19950 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
19951 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
19952
19953 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
19954 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
19955 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
19956 var targetGraph = layoutInfo.indexToGraph[targetIx];
19957
19958 if (sourceGraph != targetGraph) {
19959 // Find lowest common graph ancestor
19960 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
19961
19962 var lcaGraph = layoutInfo.graphSet[lca];
19963 var depth = 0; // Source depth
19964
19965 var tempNode = layoutInfo.layoutNodes[sourceIx];
19966
19967 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19968 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19969 depth++;
19970 } // Target depth
19971
19972
19973 tempNode = layoutInfo.layoutNodes[targetIx];
19974
19975 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19976 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19977 depth++;
19978 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
19979 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
19980 // ". Depth: " + depth);
19981 // Update idealLength
19982
19983
19984 idealLength *= depth * options.nestingFactor;
19985 }
19986
19987 tempEdge.idealLength = idealLength;
19988 tempEdge.elasticity = elasticity;
19989 layoutInfo.layoutEdges.push(tempEdge);
19990 } // Finally, return layoutInfo object
19991
19992
19993 return layoutInfo;
19994};
19995/**
19996 * @brief : This function finds the index of the lowest common
19997 * graph ancestor between 2 nodes in the subtree
19998 * (from the graph hierarchy induced tree) whose
19999 * root is graphIx
20000 *
20001 * @arg node1: node1's ID
20002 * @arg node2: node2's ID
20003 * @arg layoutInfo: layoutInfo object
20004 *
20005 */
20006
20007
20008var findLCA = function findLCA(node1, node2, layoutInfo) {
20009 // Find their common ancester, starting from the root graph
20010 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20011
20012 if (2 > res.count) {
20013 // If aux function couldn't find the common ancester,
20014 // then it is the root graph
20015 return 0;
20016 } else {
20017 return res.graph;
20018 }
20019};
20020/**
20021 * @brief : Auxiliary function used for LCA computation
20022 *
20023 * @arg node1 : node1's ID
20024 * @arg node2 : node2's ID
20025 * @arg graphIx : subgraph index
20026 * @arg layoutInfo : layoutInfo object
20027 *
20028 * @return : object of the form {count: X, graph: Y}, where:
20029 * X is the number of ancesters (max: 2) found in
20030 * graphIx (and it's subgraphs),
20031 * Y is the graph index of the lowest graph containing
20032 * all X nodes
20033 */
20034
20035
20036var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20037 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20038
20039 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20040 return {
20041 count: 2,
20042 graph: graphIx
20043 };
20044 } // Make recursive calls for all subgraphs
20045
20046
20047 var c = 0;
20048
20049 for (var i = 0; i < graph.length; i++) {
20050 var nodeId = graph[i];
20051 var nodeIx = layoutInfo.idToIndex[nodeId];
20052 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20053
20054 if (0 === children.length) {
20055 continue;
20056 }
20057
20058 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20059 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20060
20061 if (0 === result.count) {
20062 // Neither node1 nor node2 are present in this subgraph
20063 continue;
20064 } else if (1 === result.count) {
20065 // One of (node1, node2) is present in this subgraph
20066 c++;
20067
20068 if (2 === c) {
20069 // We've already found both nodes, no need to keep searching
20070 break;
20071 }
20072 } else {
20073 // Both nodes are present in this subgraph
20074 return result;
20075 }
20076 }
20077
20078 return {
20079 count: c,
20080 graph: graphIx
20081 };
20082};
20083/**
20084 * @brief: printsLayoutInfo into js console
20085 * Only used for debbuging
20086 */
20087
20088
20089if (false) {
20090 var printLayoutInfo;
20091}
20092/**
20093 * @brief : Randomizes the position of all nodes
20094 */
20095
20096
20097var randomizePositions = function randomizePositions(layoutInfo, cy) {
20098 var width = layoutInfo.clientWidth;
20099 var height = layoutInfo.clientHeight;
20100
20101 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20102 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20103
20104 if (0 === n.children.length && !n.isLocked) {
20105 n.positionX = Math.random() * width;
20106 n.positionY = Math.random() * height;
20107 }
20108 }
20109};
20110
20111var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20112 var bb = layoutInfo.boundingBox;
20113 var coseBB = {
20114 x1: Infinity,
20115 x2: -Infinity,
20116 y1: Infinity,
20117 y2: -Infinity
20118 };
20119
20120 if (options.boundingBox) {
20121 nodes.forEach(function (node) {
20122 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20123 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20124 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20125 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20126 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20127 });
20128 coseBB.w = coseBB.x2 - coseBB.x1;
20129 coseBB.h = coseBB.y2 - coseBB.y1;
20130 }
20131
20132 return function (ele, i) {
20133 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20134
20135 if (options.boundingBox) {
20136 // then add extra bounding box constraint
20137 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20138 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20139 return {
20140 x: bb.x1 + pctX * bb.w,
20141 y: bb.y1 + pctY * bb.h
20142 };
20143 } else {
20144 return {
20145 x: lnode.positionX,
20146 y: lnode.positionY
20147 };
20148 }
20149 };
20150};
20151/**
20152 * @brief : Updates the positions of nodes in the network
20153 * @arg layoutInfo : LayoutInfo object
20154 * @arg cy : Cytoscape object
20155 * @arg options : Layout options
20156 */
20157
20158
20159var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20160 // var s = 'Refreshing positions';
20161 // logDebug(s);
20162 var layout = options.layout;
20163 var nodes = options.eles.nodes();
20164 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20165 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20166
20167 if (true !== layoutInfo.ready) {
20168 // s = 'Triggering layoutready';
20169 // logDebug(s);
20170 layoutInfo.ready = true;
20171 layout.one('layoutready', options.ready);
20172 layout.emit({
20173 type: 'layoutready',
20174 layout: this
20175 });
20176 }
20177};
20178/**
20179 * @brief : Logs a debug message in JS console, if DEBUG is ON
20180 */
20181// var logDebug = function(text) {
20182// if (DEBUG) {
20183// console.debug(text);
20184// }
20185// };
20186
20187/**
20188 * @brief : Performs one iteration of the physical simulation
20189 * @arg layoutInfo : LayoutInfo object already initialized
20190 * @arg cy : Cytoscape object
20191 * @arg options : Layout options
20192 */
20193
20194
20195var step$1 = function step(layoutInfo, options, _step) {
20196 // var s = "\n\n###############################";
20197 // s += "\nSTEP: " + step;
20198 // s += "\n###############################\n";
20199 // logDebug(s);
20200 // Calculate node repulsions
20201 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20202
20203 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20204
20205 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20206
20207 propagateForces(layoutInfo); // Update positions based on calculated forces
20208
20209 updatePositions(layoutInfo);
20210};
20211/**
20212 * @brief : Computes the node repulsion forces
20213 */
20214
20215
20216var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20217 // Go through each of the graphs in graphSet
20218 // Nodes only repel each other if they belong to the same graph
20219 // var s = 'calculateNodeForces';
20220 // logDebug(s);
20221 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20222 var graph = layoutInfo.graphSet[i];
20223 var numNodes = graph.length; // s = "Set: " + graph.toString();
20224 // logDebug(s);
20225 // Now get all the pairs of nodes
20226 // Only get each pair once, (A, B) = (B, A)
20227
20228 for (var j = 0; j < numNodes; j++) {
20229 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20230
20231 for (var k = j + 1; k < numNodes; k++) {
20232 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20233 nodeRepulsion(node1, node2, layoutInfo, options);
20234 }
20235 }
20236 }
20237};
20238
20239var randomDistance = function randomDistance(max) {
20240 return -max + 2 * max * Math.random();
20241};
20242/**
20243 * @brief : Compute the node repulsion forces between a pair of nodes
20244 */
20245
20246
20247var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20248 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20249 var cmptId1 = node1.cmptId;
20250 var cmptId2 = node2.cmptId;
20251
20252 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20253 return;
20254 } // Get direction of line connecting both node centers
20255
20256
20257 var directionX = node2.positionX - node1.positionX;
20258 var directionY = node2.positionY - node1.positionY;
20259 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20260 // If both centers are the same, apply a random force
20261
20262 if (0 === directionX && 0 === directionY) {
20263 directionX = randomDistance(maxRandDist);
20264 directionY = randomDistance(maxRandDist);
20265 }
20266
20267 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20268
20269 if (overlap > 0) {
20270 // s += "\nNodes DO overlap.";
20271 // s += "\nOverlap: " + overlap;
20272 // If nodes overlap, repulsion force is proportional
20273 // to the overlap
20274 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
20275
20276 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
20277
20278 var forceX = force * directionX / distance;
20279 var forceY = force * directionY / distance;
20280 } else {
20281 // s += "\nNodes do NOT overlap.";
20282 // If there's no overlap, force is inversely proportional
20283 // to squared distance
20284 // Get clipping points for both nodes
20285 var point1 = findClippingPoint(node1, directionX, directionY);
20286 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
20287
20288 var distanceX = point2.x - point1.x;
20289 var distanceY = point2.y - point1.y;
20290 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20291 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
20292 // Compute the module and components of the force vector
20293
20294 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20295 var forceX = force * distanceX / distance;
20296 var forceY = force * distanceY / distance;
20297 } // Apply force
20298
20299
20300 if (!node1.isLocked) {
20301 node1.offsetX -= forceX;
20302 node1.offsetY -= forceY;
20303 }
20304
20305 if (!node2.isLocked) {
20306 node2.offsetX += forceX;
20307 node2.offsetY += forceY;
20308 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20309 // logDebug(s);
20310
20311
20312 return;
20313};
20314/**
20315 * @brief : Determines whether two nodes overlap or not
20316 * @return : Amount of overlapping (0 => no overlap)
20317 */
20318
20319
20320var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20321 if (dX > 0) {
20322 var overlapX = node1.maxX - node2.minX;
20323 } else {
20324 var overlapX = node2.maxX - node1.minX;
20325 }
20326
20327 if (dY > 0) {
20328 var overlapY = node1.maxY - node2.minY;
20329 } else {
20330 var overlapY = node2.maxY - node1.minY;
20331 }
20332
20333 if (overlapX >= 0 && overlapY >= 0) {
20334 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20335 } else {
20336 return 0;
20337 }
20338};
20339/**
20340 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20341 * the rectangular bounding box of it's source/target node
20342 */
20343
20344
20345var findClippingPoint = function findClippingPoint(node, dX, dY) {
20346 // Shorcuts
20347 var X = node.positionX;
20348 var Y = node.positionY;
20349 var H = node.height || 1;
20350 var W = node.width || 1;
20351 var dirSlope = dY / dX;
20352 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
20353 // " . Height: " + H + ", Width: " + W +
20354 // "\nDirection " + dX + ", " + dY;
20355 //
20356 // Compute intersection
20357
20358 var res = {}; // Case: Vertical direction (up)
20359
20360 if (0 === dX && 0 < dY) {
20361 res.x = X; // s += "\nUp direction";
20362
20363 res.y = Y + H / 2;
20364 return res;
20365 } // Case: Vertical direction (down)
20366
20367
20368 if (0 === dX && 0 > dY) {
20369 res.x = X;
20370 res.y = Y + H / 2; // s += "\nDown direction";
20371
20372 return res;
20373 } // Case: Intersects the right border
20374
20375
20376 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20377 res.x = X + W / 2;
20378 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
20379
20380 return res;
20381 } // Case: Intersects the left border
20382
20383
20384 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20385 res.x = X - W / 2;
20386 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
20387
20388 return res;
20389 } // Case: Intersects the top border
20390
20391
20392 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20393 res.x = X + H * dX / 2 / dY;
20394 res.y = Y + H / 2; // s += "\nTop border";
20395
20396 return res;
20397 } // Case: Intersects the bottom border
20398
20399
20400 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20401 res.x = X - H * dX / 2 / dY;
20402 res.y = Y - H / 2; // s += "\nBottom border";
20403
20404 return res;
20405 } // s += "\nClipping point found at " + res.x + ", " + res.y;
20406 // logDebug(s);
20407
20408
20409 return res;
20410};
20411/**
20412 * @brief : Calculates all edge forces
20413 */
20414
20415
20416var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20417 // Iterate over all edges
20418 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20419 // Get edge, source & target nodes
20420 var edge = layoutInfo.layoutEdges[i];
20421 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20422 var source = layoutInfo.layoutNodes[sourceIx];
20423 var targetIx = layoutInfo.idToIndex[edge.targetId];
20424 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
20425
20426 var directionX = target.positionX - source.positionX;
20427 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
20428 // A random force has already been applied as node repulsion
20429
20430 if (0 === directionX && 0 === directionY) {
20431 continue;
20432 } // Get clipping points for both nodes
20433
20434
20435 var point1 = findClippingPoint(source, directionX, directionY);
20436 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20437 var lx = point2.x - point1.x;
20438 var ly = point2.y - point1.y;
20439 var l = Math.sqrt(lx * lx + ly * ly);
20440 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20441
20442 if (0 !== l) {
20443 var forceX = force * lx / l;
20444 var forceY = force * ly / l;
20445 } else {
20446 var forceX = 0;
20447 var forceY = 0;
20448 } // Add this force to target and source nodes
20449
20450
20451 if (!source.isLocked) {
20452 source.offsetX += forceX;
20453 source.offsetY += forceY;
20454 }
20455
20456 if (!target.isLocked) {
20457 target.offsetX -= forceX;
20458 target.offsetY -= forceY;
20459 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20460 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20461 // logDebug(s);
20462
20463 }
20464};
20465/**
20466 * @brief : Computes gravity forces for all nodes
20467 */
20468
20469
20470var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20471 var distThreshold = 1; // var s = 'calculateGravityForces';
20472 // logDebug(s);
20473
20474 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20475 var graph = layoutInfo.graphSet[i];
20476 var numNodes = graph.length; // s = "Set: " + graph.toString();
20477 // logDebug(s);
20478 // Compute graph center
20479
20480 if (0 === i) {
20481 var centerX = layoutInfo.clientHeight / 2;
20482 var centerY = layoutInfo.clientWidth / 2;
20483 } else {
20484 // Get Parent node for this graph, and use its position as center
20485 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20486 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20487 var centerX = parent.positionX;
20488 var centerY = parent.positionY;
20489 } // s = "Center found at: " + centerX + ", " + centerY;
20490 // logDebug(s);
20491 // Apply force to all nodes in graph
20492
20493
20494 for (var j = 0; j < numNodes; j++) {
20495 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
20496
20497 if (node.isLocked) {
20498 continue;
20499 }
20500
20501 var dx = centerX - node.positionX;
20502 var dy = centerY - node.positionY;
20503 var d = Math.sqrt(dx * dx + dy * dy);
20504
20505 if (d > distThreshold) {
20506 var fx = options.gravity * dx / d;
20507 var fy = options.gravity * dy / d;
20508 node.offsetX += fx;
20509 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
20510 } // s += ": skypped since it's too close to center";
20511 // logDebug(s);
20512
20513 }
20514 }
20515};
20516/**
20517 * @brief : This function propagates the existing offsets from
20518 * parent nodes to its descendents.
20519 * @arg layoutInfo : layoutInfo Object
20520 * @arg cy : cytoscape Object
20521 * @arg options : Layout options
20522 */
20523
20524
20525var propagateForces = function propagateForces(layoutInfo, options) {
20526 // Inline implementation of a queue, used for traversing the graph in BFS order
20527 var queue = [];
20528 var start = 0; // Points to the start the queue
20529
20530 var end = -1; // Points to the end of the queue
20531 // logDebug('propagateForces');
20532 // Start by visiting the nodes in the root graph
20533
20534 queue.push.apply(queue, layoutInfo.graphSet[0]);
20535 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
20536
20537 while (start <= end) {
20538 // Get the node to visit and remove it from queue
20539 var nodeId = queue[start++];
20540 var nodeIndex = layoutInfo.idToIndex[nodeId];
20541 var node = layoutInfo.layoutNodes[nodeIndex];
20542 var children = node.children; // We only need to process the node if it's compound
20543
20544 if (0 < children.length && !node.isLocked) {
20545 var offX = node.offsetX;
20546 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
20547 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20548 // s += "\n Children: " + children.toString();
20549 // logDebug(s);
20550
20551 for (var i = 0; i < children.length; i++) {
20552 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
20553
20554 childNode.offsetX += offX;
20555 childNode.offsetY += offY; // Add children to queue to be visited
20556
20557 queue[++end] = children[i];
20558 } // Reset parent offsets
20559
20560
20561 node.offsetX = 0;
20562 node.offsetY = 0;
20563 }
20564 }
20565};
20566/**
20567 * @brief : Updates the layout model positions, based on
20568 * the accumulated forces
20569 */
20570
20571
20572var updatePositions = function updatePositions(layoutInfo, options) {
20573 // var s = 'Updating positions';
20574 // logDebug(s);
20575 // Reset boundaries for compound nodes
20576 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20577 var n = layoutInfo.layoutNodes[i];
20578
20579 if (0 < n.children.length) {
20580 // logDebug("Resetting boundaries of compound node: " + n.id);
20581 n.maxX = undefined;
20582 n.minX = undefined;
20583 n.maxY = undefined;
20584 n.minY = undefined;
20585 }
20586 }
20587
20588 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20589 var n = layoutInfo.layoutNodes[i];
20590
20591 if (0 < n.children.length || n.isLocked) {
20592 // No need to set compound or locked node position
20593 // logDebug("Skipping position update of node: " + n.id);
20594 continue;
20595 } // s = "Node: " + n.id + " Previous position: (" +
20596 // n.positionX + ", " + n.positionY + ").";
20597 // Limit displacement in order to improve stability
20598
20599
20600 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20601 n.positionX += tempForce.x;
20602 n.positionY += tempForce.y;
20603 n.offsetX = 0;
20604 n.offsetY = 0;
20605 n.minX = n.positionX - n.width;
20606 n.maxX = n.positionX + n.width;
20607 n.minY = n.positionY - n.height;
20608 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20609 // logDebug(s);
20610 // Update ancestry boudaries
20611
20612 updateAncestryBoundaries(n, layoutInfo);
20613 } // Update size, position of compund nodes
20614
20615
20616 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20617 var n = layoutInfo.layoutNodes[i];
20618
20619 if (0 < n.children.length && !n.isLocked) {
20620 n.positionX = (n.maxX + n.minX) / 2;
20621 n.positionY = (n.maxY + n.minY) / 2;
20622 n.width = n.maxX - n.minX;
20623 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
20624 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20625 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20626 // logDebug(s);
20627 }
20628 }
20629};
20630/**
20631 * @brief : Limits a force (forceX, forceY) to be not
20632 * greater (in modulo) than max.
20633 8 Preserves force direction.
20634 */
20635
20636
20637var limitForce = function limitForce(forceX, forceY, max) {
20638 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20639 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20640
20641 if (force > max) {
20642 var res = {
20643 x: max * forceX / force,
20644 y: max * forceY / force
20645 };
20646 } else {
20647 var res = {
20648 x: forceX,
20649 y: forceY
20650 };
20651 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20652 // logDebug(s);
20653
20654
20655 return res;
20656};
20657/**
20658 * @brief : Function used for keeping track of compound node
20659 * sizes, since they should bound all their subnodes.
20660 */
20661
20662
20663var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20664 // var s = "Propagating new position/size of node " + node.id;
20665 var parentId = node.parentId;
20666
20667 if (null == parentId) {
20668 // If there's no parent, we are done
20669 // s += ". No parent node.";
20670 // logDebug(s);
20671 return;
20672 } // Get Parent Node
20673
20674
20675 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20676 var flag = false; // MaxX
20677
20678 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20679 p.maxX = node.maxX + p.padRight;
20680 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20681 } // MinX
20682
20683
20684 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20685 p.minX = node.minX - p.padLeft;
20686 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20687 } // MaxY
20688
20689
20690 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20691 p.maxY = node.maxY + p.padBottom;
20692 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20693 } // MinY
20694
20695
20696 if (null == p.minY || node.minY - p.padTop < p.minY) {
20697 p.minY = node.minY - p.padTop;
20698 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20699 } // If updated boundaries, propagate changes upward
20700
20701
20702 if (flag) {
20703 // logDebug(s);
20704 return updateAncestryBoundaries(p, layoutInfo);
20705 } // s += ". No changes in boundaries/position of parent node " + p.id;
20706 // logDebug(s);
20707
20708
20709 return;
20710};
20711
20712var separateComponents = function separateComponents(layoutInfo, options) {
20713 var nodes = layoutInfo.layoutNodes;
20714 var components = [];
20715
20716 for (var i = 0; i < nodes.length; i++) {
20717 var node = nodes[i];
20718 var cid = node.cmptId;
20719 var component = components[cid] = components[cid] || [];
20720 component.push(node);
20721 }
20722
20723 var totalA = 0;
20724
20725 for (var i = 0; i < components.length; i++) {
20726 var c = components[i];
20727
20728 if (!c) {
20729 continue;
20730 }
20731
20732 c.x1 = Infinity;
20733 c.x2 = -Infinity;
20734 c.y1 = Infinity;
20735 c.y2 = -Infinity;
20736
20737 for (var j = 0; j < c.length; j++) {
20738 var n = c[j];
20739 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
20740 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
20741 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
20742 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
20743 }
20744
20745 c.w = c.x2 - c.x1;
20746 c.h = c.y2 - c.y1;
20747 totalA += c.w * c.h;
20748 }
20749
20750 components.sort(function (c1, c2) {
20751 return c2.w * c2.h - c1.w * c1.h;
20752 });
20753 var x = 0;
20754 var y = 0;
20755 var usedW = 0;
20756 var rowH = 0;
20757 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
20758
20759 for (var i = 0; i < components.length; i++) {
20760 var c = components[i];
20761
20762 if (!c) {
20763 continue;
20764 }
20765
20766 for (var j = 0; j < c.length; j++) {
20767 var n = c[j];
20768
20769 if (!n.isLocked) {
20770 n.positionX += x - c.x1;
20771 n.positionY += y - c.y1;
20772 }
20773 }
20774
20775 x += c.w + options.componentSpacing;
20776 usedW += c.w + options.componentSpacing;
20777 rowH = Math.max(rowH, c.h);
20778
20779 if (usedW > maxRowW) {
20780 y += rowH + options.componentSpacing;
20781 x = 0;
20782 usedW = 0;
20783 rowH = 0;
20784 }
20785 }
20786};
20787
20788var defaults$d = {
20789 fit: true,
20790 // whether to fit the viewport to the graph
20791 padding: 30,
20792 // padding used on fit
20793 boundingBox: undefined,
20794 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20795 avoidOverlap: true,
20796 // prevents node overlap, may overflow boundingBox if not enough space
20797 avoidOverlapPadding: 10,
20798 // extra spacing around nodes when avoidOverlap: true
20799 nodeDimensionsIncludeLabels: false,
20800 // Excludes the label when calculating node bounding boxes for the layout algorithm
20801 spacingFactor: undefined,
20802 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20803 condense: false,
20804 // uses all available space on false, uses minimal space on true
20805 rows: undefined,
20806 // force num of rows in the grid
20807 cols: undefined,
20808 // force num of columns in the grid
20809 position: function position(node) {},
20810 // returns { row, col } for element
20811 sort: undefined,
20812 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20813 animate: false,
20814 // whether to transition the node positions
20815 animationDuration: 500,
20816 // duration of animation in ms if enabled
20817 animationEasing: undefined,
20818 // easing of animation if enabled
20819 animateFilter: function animateFilter(node, i) {
20820 return true;
20821 },
20822 // 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
20823 ready: undefined,
20824 // callback on layoutready
20825 stop: undefined,
20826 // callback on layoutstop
20827 transform: function transform(node, position) {
20828 return position;
20829 } // transform a given node position. Useful for changing flow direction in discrete layouts
20830
20831};
20832
20833function GridLayout(options) {
20834 this.options = extend({}, defaults$d, options);
20835}
20836
20837GridLayout.prototype.run = function () {
20838 var params = this.options;
20839 var options = params;
20840 var cy = params.cy;
20841 var eles = options.eles;
20842 var nodes = eles.nodes().not(':parent');
20843
20844 if (options.sort) {
20845 nodes = nodes.sort(options.sort);
20846 }
20847
20848 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20849 x1: 0,
20850 y1: 0,
20851 w: cy.width(),
20852 h: cy.height()
20853 });
20854
20855 if (bb.h === 0 || bb.w === 0) {
20856 nodes.layoutPositions(this, options, function (ele) {
20857 return {
20858 x: bb.x1,
20859 y: bb.y1
20860 };
20861 });
20862 } else {
20863 // width/height * splits^2 = cells where splits is number of times to split width
20864 var cells = nodes.size();
20865 var splits = Math.sqrt(cells * bb.h / bb.w);
20866 var rows = Math.round(splits);
20867 var cols = Math.round(bb.w / bb.h * splits);
20868
20869 var small = function small(val) {
20870 if (val == null) {
20871 return Math.min(rows, cols);
20872 } else {
20873 var min = Math.min(rows, cols);
20874
20875 if (min == rows) {
20876 rows = val;
20877 } else {
20878 cols = val;
20879 }
20880 }
20881 };
20882
20883 var large = function large(val) {
20884 if (val == null) {
20885 return Math.max(rows, cols);
20886 } else {
20887 var max = Math.max(rows, cols);
20888
20889 if (max == rows) {
20890 rows = val;
20891 } else {
20892 cols = val;
20893 }
20894 }
20895 };
20896
20897 var oRows = options.rows;
20898 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
20899
20900 if (oRows != null && oCols != null) {
20901 rows = oRows;
20902 cols = oCols;
20903 } else if (oRows != null && oCols == null) {
20904 rows = oRows;
20905 cols = Math.ceil(cells / rows);
20906 } else if (oRows == null && oCols != null) {
20907 cols = oCols;
20908 rows = Math.ceil(cells / cols);
20909 } // otherwise use the automatic values and adjust accordingly
20910 // if rounding was up, see if we can reduce rows or columns
20911 else if (cols * rows > cells) {
20912 var sm = small();
20913 var lg = large(); // reducing the small side takes away the most cells, so try it first
20914
20915 if ((sm - 1) * lg >= cells) {
20916 small(sm - 1);
20917 } else if ((lg - 1) * sm >= cells) {
20918 large(lg - 1);
20919 }
20920 } else {
20921 // if rounding was too low, add rows or columns
20922 while (cols * rows < cells) {
20923 var _sm = small();
20924
20925 var _lg = large(); // try to add to larger side first (adds less in multiplication)
20926
20927
20928 if ((_lg + 1) * _sm >= cells) {
20929 large(_lg + 1);
20930 } else {
20931 small(_sm + 1);
20932 }
20933 }
20934 }
20935
20936 var cellWidth = bb.w / cols;
20937 var cellHeight = bb.h / rows;
20938
20939 if (options.condense) {
20940 cellWidth = 0;
20941 cellHeight = 0;
20942 }
20943
20944 if (options.avoidOverlap) {
20945 for (var i = 0; i < nodes.length; i++) {
20946 var node = nodes[i];
20947 var pos = node._private.position;
20948
20949 if (pos.x == null || pos.y == null) {
20950 // for bb
20951 pos.x = 0;
20952 pos.y = 0;
20953 }
20954
20955 var nbb = node.layoutDimensions(options);
20956 var p = options.avoidOverlapPadding;
20957 var w = nbb.w + p;
20958 var h = nbb.h + p;
20959 cellWidth = Math.max(cellWidth, w);
20960 cellHeight = Math.max(cellHeight, h);
20961 }
20962 }
20963
20964 var cellUsed = {}; // e.g. 'c-0-2' => true
20965
20966 var used = function used(row, col) {
20967 return cellUsed['c-' + row + '-' + col] ? true : false;
20968 };
20969
20970 var use = function use(row, col) {
20971 cellUsed['c-' + row + '-' + col] = true;
20972 }; // to keep track of current cell position
20973
20974
20975 var row = 0;
20976 var col = 0;
20977
20978 var moveToNextCell = function moveToNextCell() {
20979 col++;
20980
20981 if (col >= cols) {
20982 col = 0;
20983 row++;
20984 }
20985 }; // get a cache of all the manual positions
20986
20987
20988 var id2manPos = {};
20989
20990 for (var _i = 0; _i < nodes.length; _i++) {
20991 var _node = nodes[_i];
20992 var rcPos = options.position(_node);
20993
20994 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
20995 // must have at least row or col def'd
20996 var _pos = {
20997 row: rcPos.row,
20998 col: rcPos.col
20999 };
21000
21001 if (_pos.col === undefined) {
21002 // find unused col
21003 _pos.col = 0;
21004
21005 while (used(_pos.row, _pos.col)) {
21006 _pos.col++;
21007 }
21008 } else if (_pos.row === undefined) {
21009 // find unused row
21010 _pos.row = 0;
21011
21012 while (used(_pos.row, _pos.col)) {
21013 _pos.row++;
21014 }
21015 }
21016
21017 id2manPos[_node.id()] = _pos;
21018 use(_pos.row, _pos.col);
21019 }
21020 }
21021
21022 var getPos = function getPos(element, i) {
21023 var x, y;
21024
21025 if (element.locked() || element.isParent()) {
21026 return false;
21027 } // see if we have a manual position set
21028
21029
21030 var rcPos = id2manPos[element.id()];
21031
21032 if (rcPos) {
21033 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21034 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21035 } else {
21036 // otherwise set automatically
21037 while (used(row, col)) {
21038 moveToNextCell();
21039 }
21040
21041 x = col * cellWidth + cellWidth / 2 + bb.x1;
21042 y = row * cellHeight + cellHeight / 2 + bb.y1;
21043 use(row, col);
21044 moveToNextCell();
21045 }
21046
21047 return {
21048 x: x,
21049 y: y
21050 };
21051 };
21052
21053 nodes.layoutPositions(this, options, getPos);
21054 }
21055
21056 return this; // chaining
21057};
21058
21059var defaults$e = {
21060 ready: function ready() {},
21061 // on layoutready
21062 stop: function stop() {} // on layoutstop
21063
21064}; // constructor
21065// options : object containing layout options
21066
21067function NullLayout(options) {
21068 this.options = extend({}, defaults$e, options);
21069} // runs the layout
21070
21071
21072NullLayout.prototype.run = function () {
21073 var options = this.options;
21074 var eles = options.eles; // elements to consider in the layout
21075
21076 var layout = this; // cy is automatically populated for us in the constructor
21077 // (disable eslint for next line as this serves as example layout code to external developers)
21078 // eslint-disable-next-line no-unused-vars
21079
21080 var cy = options.cy;
21081 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21082 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21083
21084 eles.nodes().positions(function () {
21085 return {
21086 x: 0,
21087 y: 0
21088 };
21089 }); // trigger layoutready when each node has had its position set at least once
21090
21091 layout.one('layoutready', options.ready);
21092 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21093
21094 layout.one('layoutstop', options.stop);
21095 layout.emit('layoutstop');
21096 return this; // chaining
21097}; // called on continuous layouts to stop them before they finish
21098
21099
21100NullLayout.prototype.stop = function () {
21101 return this; // chaining
21102};
21103
21104var defaults$f = {
21105 positions: undefined,
21106 // map of (node id) => (position obj); or function(node){ return somPos; }
21107 zoom: undefined,
21108 // the zoom level to set (prob want fit = false if set)
21109 pan: undefined,
21110 // the pan level to set (prob want fit = false if set)
21111 fit: true,
21112 // whether to fit to viewport
21113 padding: 30,
21114 // padding on fit
21115 animate: false,
21116 // whether to transition the node positions
21117 animationDuration: 500,
21118 // duration of animation in ms if enabled
21119 animationEasing: undefined,
21120 // easing of animation if enabled
21121 animateFilter: function animateFilter(node, i) {
21122 return true;
21123 },
21124 // 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
21125 ready: undefined,
21126 // callback on layoutready
21127 stop: undefined,
21128 // callback on layoutstop
21129 transform: function transform(node, position) {
21130 return position;
21131 } // transform a given node position. Useful for changing flow direction in discrete layouts
21132
21133};
21134
21135function PresetLayout(options) {
21136 this.options = extend({}, defaults$f, options);
21137}
21138
21139PresetLayout.prototype.run = function () {
21140 var options = this.options;
21141 var eles = options.eles;
21142 var nodes = eles.nodes();
21143 var posIsFn = fn(options.positions);
21144
21145 function getPosition(node) {
21146 if (options.positions == null) {
21147 return copyPosition(node.position());
21148 }
21149
21150 if (posIsFn) {
21151 return options.positions(node);
21152 }
21153
21154 var pos = options.positions[node._private.data.id];
21155
21156 if (pos == null) {
21157 return null;
21158 }
21159
21160 return pos;
21161 }
21162
21163 nodes.layoutPositions(this, options, function (node, i) {
21164 var position = getPosition(node);
21165
21166 if (node.locked() || position == null) {
21167 return false;
21168 }
21169
21170 return position;
21171 });
21172 return this; // chaining
21173};
21174
21175var defaults$g = {
21176 fit: true,
21177 // whether to fit to viewport
21178 padding: 30,
21179 // fit padding
21180 boundingBox: undefined,
21181 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21182 animate: false,
21183 // whether to transition the node positions
21184 animationDuration: 500,
21185 // duration of animation in ms if enabled
21186 animationEasing: undefined,
21187 // easing of animation if enabled
21188 animateFilter: function animateFilter(node, i) {
21189 return true;
21190 },
21191 // 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
21192 ready: undefined,
21193 // callback on layoutready
21194 stop: undefined,
21195 // callback on layoutstop
21196 transform: function transform(node, position) {
21197 return position;
21198 } // transform a given node position. Useful for changing flow direction in discrete layouts
21199
21200};
21201
21202function RandomLayout(options) {
21203 this.options = extend({}, defaults$g, options);
21204}
21205
21206RandomLayout.prototype.run = function () {
21207 var options = this.options;
21208 var cy = options.cy;
21209 var eles = options.eles;
21210 var nodes = eles.nodes().not(':parent');
21211 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21212 x1: 0,
21213 y1: 0,
21214 w: cy.width(),
21215 h: cy.height()
21216 });
21217
21218 var getPos = function getPos(node, i) {
21219 return {
21220 x: bb.x1 + Math.round(Math.random() * bb.w),
21221 y: bb.y1 + Math.round(Math.random() * bb.h)
21222 };
21223 };
21224
21225 nodes.layoutPositions(this, options, getPos);
21226 return this; // chaining
21227};
21228
21229var layout = [{
21230 name: 'breadthfirst',
21231 impl: BreadthFirstLayout
21232}, {
21233 name: 'circle',
21234 impl: CircleLayout
21235}, {
21236 name: 'concentric',
21237 impl: ConcentricLayout
21238}, {
21239 name: 'cose',
21240 impl: CoseLayout
21241}, {
21242 name: 'grid',
21243 impl: GridLayout
21244}, {
21245 name: 'null',
21246 impl: NullLayout
21247}, {
21248 name: 'preset',
21249 impl: PresetLayout
21250}, {
21251 name: 'random',
21252 impl: RandomLayout
21253}];
21254
21255function NullRenderer(options) {
21256 this.options = options;
21257 this.notifications = 0; // for testing
21258}
21259
21260var noop$1 = function noop() {};
21261
21262var throwImgErr = function throwImgErr() {
21263 throw new Error('A headless instance can not render images');
21264};
21265
21266NullRenderer.prototype = {
21267 recalculateRenderedStyle: noop$1,
21268 notify: function notify() {
21269 this.notifications++;
21270 },
21271 init: noop$1,
21272 isHeadless: function isHeadless() {
21273 return true;
21274 },
21275 png: throwImgErr,
21276 jpg: throwImgErr
21277};
21278
21279var BRp = {};
21280BRp.arrowShapeWidth = 0.3;
21281
21282BRp.registerArrowShapes = function () {
21283 var arrowShapes = this.arrowShapes = {};
21284 var renderer = this; // Contract for arrow shapes:
21285 // 0, 0 is arrow tip
21286 // (0, 1) is direction towards node
21287 // (1, 0) is right
21288 //
21289 // functional api:
21290 // collide: check x, y in shape
21291 // roughCollide: called before collide, no false negatives
21292 // draw: draw
21293 // spacing: dist(arrowTip, nodeBoundary)
21294 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21295
21296 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21297 var x1 = translation.x - size / 2 - padding;
21298 var x2 = translation.x + size / 2 + padding;
21299 var y1 = translation.y - size / 2 - padding;
21300 var y2 = translation.y + size / 2 + padding;
21301 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21302 return inside;
21303 };
21304
21305 var transform = function transform(x, y, size, angle, translation) {
21306 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21307 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21308 var xScaled = xRotated * size;
21309 var yScaled = yRotated * size;
21310 var xTranslated = xScaled + translation.x;
21311 var yTranslated = yScaled + translation.y;
21312 return {
21313 x: xTranslated,
21314 y: yTranslated
21315 };
21316 };
21317
21318 var transformPoints = function transformPoints(pts, size, angle, translation) {
21319 var retPts = [];
21320
21321 for (var i = 0; i < pts.length; i += 2) {
21322 var x = pts[i];
21323 var y = pts[i + 1];
21324 retPts.push(transform(x, y, size, angle, translation));
21325 }
21326
21327 return retPts;
21328 };
21329
21330 var pointsToArr = function pointsToArr(pts) {
21331 var ret = [];
21332
21333 for (var i = 0; i < pts.length; i++) {
21334 var p = pts[i];
21335 ret.push(p.x, p.y);
21336 }
21337
21338 return ret;
21339 };
21340
21341 var standardGap = function standardGap(edge) {
21342 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21343 };
21344
21345 var defineArrowShape = function defineArrowShape(name, defn) {
21346 if (string(defn)) {
21347 defn = arrowShapes[defn];
21348 }
21349
21350 arrowShapes[name] = extend({
21351 name: name,
21352 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21353 collide: function collide(x, y, size, angle, translation, padding) {
21354 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21355 var inside = pointInsidePolygonPoints(x, y, points);
21356 return inside;
21357 },
21358 roughCollide: bbCollide,
21359 draw: function draw(context, size, angle, translation) {
21360 var points = transformPoints(this.points, size, angle, translation);
21361 renderer.arrowShapeImpl('polygon')(context, points);
21362 },
21363 spacing: function spacing(edge) {
21364 return 0;
21365 },
21366 gap: standardGap
21367 }, defn);
21368 };
21369
21370 defineArrowShape('none', {
21371 collide: falsify,
21372 roughCollide: falsify,
21373 draw: noop,
21374 spacing: zeroify,
21375 gap: zeroify
21376 });
21377 defineArrowShape('triangle', {
21378 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21379 });
21380 defineArrowShape('arrow', 'triangle');
21381 defineArrowShape('triangle-backcurve', {
21382 points: arrowShapes['triangle'].points,
21383 controlPoint: [0, -0.15],
21384 roughCollide: bbCollide,
21385 draw: function draw(context, size, angle, translation, edgeWidth) {
21386 var ptsTrans = transformPoints(this.points, size, angle, translation);
21387 var ctrlPt = this.controlPoint;
21388 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21389 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21390 },
21391 gap: function gap(edge) {
21392 return standardGap(edge) * 0.8;
21393 }
21394 });
21395 defineArrowShape('triangle-tee', {
21396 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21397 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21398 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21399 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21400 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21401 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21402 return inside;
21403 },
21404 draw: function draw(context, size, angle, translation, edgeWidth) {
21405 var triPts = transformPoints(this.points, size, angle, translation);
21406 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21407 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21408 }
21409 });
21410 defineArrowShape('circle-triangle', {
21411 radius: 0.15,
21412 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
21413 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21414 var t = translation;
21415 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21416 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21417 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
21418 },
21419 draw: function draw(context, size, angle, translation, edgeWidth) {
21420 var triPts = transformPoints(this.pointsTr, size, angle, translation);
21421 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
21422 },
21423 spacing: function spacing(edge) {
21424 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21425 }
21426 });
21427 defineArrowShape('triangle-cross', {
21428 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21429 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
21430 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
21431 0.15, -0.4],
21432 crossLinePts: function crossLinePts(size, edgeWidth) {
21433 // shift points so that the distance between the cross points matches edge width
21434 var p = this.baseCrossLinePts.slice();
21435 var shiftFactor = edgeWidth / size;
21436 var y0 = 3;
21437 var y1 = 5;
21438 p[y0] = p[y0] - shiftFactor;
21439 p[y1] = p[y1] - shiftFactor;
21440 return p;
21441 },
21442 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21443 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21444 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21445 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21446 return inside;
21447 },
21448 draw: function draw(context, size, angle, translation, edgeWidth) {
21449 var triPts = transformPoints(this.points, size, angle, translation);
21450 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21451 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21452 }
21453 });
21454 defineArrowShape('vee', {
21455 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21456 gap: function gap(edge) {
21457 return standardGap(edge) * 0.525;
21458 }
21459 });
21460 defineArrowShape('circle', {
21461 radius: 0.15,
21462 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21463 var t = translation;
21464 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21465 return inside;
21466 },
21467 draw: function draw(context, size, angle, translation, edgeWidth) {
21468 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21469 },
21470 spacing: function spacing(edge) {
21471 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21472 }
21473 });
21474 defineArrowShape('tee', {
21475 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21476 spacing: function spacing(edge) {
21477 return 1;
21478 },
21479 gap: function gap(edge) {
21480 return 1;
21481 }
21482 });
21483 defineArrowShape('square', {
21484 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21485 });
21486 defineArrowShape('diamond', {
21487 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21488 gap: function gap(edge) {
21489 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21490 }
21491 });
21492 defineArrowShape('chevron', {
21493 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21494 gap: function gap(edge) {
21495 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21496 }
21497 });
21498};
21499
21500var BRp$1 = {}; // Project mouse
21501
21502BRp$1.projectIntoViewport = function (clientX, clientY) {
21503 var cy = this.cy;
21504 var offsets = this.findContainerClientCoords();
21505 var offsetLeft = offsets[0];
21506 var offsetTop = offsets[1];
21507 var scale = offsets[4];
21508 var pan = cy.pan();
21509 var zoom = cy.zoom();
21510 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21511 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21512 return [x, y];
21513};
21514
21515BRp$1.findContainerClientCoords = function () {
21516 if (this.containerBB) {
21517 return this.containerBB;
21518 }
21519
21520 var container = this.container;
21521 var rect = container.getBoundingClientRect();
21522 var style = window$1.getComputedStyle(container);
21523
21524 var styleValue = function styleValue(name) {
21525 return parseFloat(style.getPropertyValue(name));
21526 };
21527
21528 var padding = {
21529 left: styleValue('padding-left'),
21530 right: styleValue('padding-right'),
21531 top: styleValue('padding-top'),
21532 bottom: styleValue('padding-bottom')
21533 };
21534 var border = {
21535 left: styleValue('border-left-width'),
21536 right: styleValue('border-right-width'),
21537 top: styleValue('border-top-width'),
21538 bottom: styleValue('border-bottom-width')
21539 };
21540 var clientWidth = container.clientWidth;
21541 var clientHeight = container.clientHeight;
21542 var paddingHor = padding.left + padding.right;
21543 var paddingVer = padding.top + padding.bottom;
21544 var borderHor = border.left + border.right;
21545 var scale = rect.width / (clientWidth + borderHor);
21546 var unscaledW = clientWidth - paddingHor;
21547 var unscaledH = clientHeight - paddingVer;
21548 var left = rect.left + padding.left + border.left;
21549 var top = rect.top + padding.top + border.top;
21550 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21551};
21552
21553BRp$1.invalidateContainerClientCoordsCache = function () {
21554 this.containerBB = null;
21555};
21556
21557BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21558 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21559};
21560
21561BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21562 var self = this;
21563 var r = this;
21564 var eles = r.getCachedZSortedEles();
21565 var near = []; // 1 node max, 1 edge max
21566
21567 var zoom = r.cy.zoom();
21568 var hasCompounds = r.cy.hasCompoundNodes();
21569 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21570 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21571 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21572 var minSqDist = Infinity;
21573 var nearEdge;
21574 var nearNode;
21575
21576 if (interactiveElementsOnly) {
21577 eles = eles.interactive;
21578 }
21579
21580 function addEle(ele, sqDist) {
21581 if (ele.isNode()) {
21582 if (nearNode) {
21583 return; // can't replace node
21584 } else {
21585 nearNode = ele;
21586 near.push(ele);
21587 }
21588 }
21589
21590 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21591 if (nearEdge) {
21592 // then replace existing edge
21593 // can replace only if same z-index
21594 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) {
21595 for (var i = 0; i < near.length; i++) {
21596 if (near[i].isEdge()) {
21597 near[i] = ele;
21598 nearEdge = ele;
21599 minSqDist = sqDist != null ? sqDist : minSqDist;
21600 break;
21601 }
21602 }
21603 }
21604 } else {
21605 near.push(ele);
21606 nearEdge = ele;
21607 minSqDist = sqDist != null ? sqDist : minSqDist;
21608 }
21609 }
21610 }
21611
21612 function checkNode(node) {
21613 var width = node.outerWidth() + 2 * nodeThreshold;
21614 var height = node.outerHeight() + 2 * nodeThreshold;
21615 var hw = width / 2;
21616 var hh = height / 2;
21617 var pos = node.position();
21618
21619 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21620 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21621 ) {
21622 var shape = r.nodeShapes[self.getNodeShape(node)];
21623
21624 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
21625 addEle(node, 0);
21626 return true;
21627 }
21628 }
21629 }
21630
21631 function checkEdge(edge) {
21632 var _p = edge._private;
21633 var rs = _p.rscratch;
21634 var styleWidth = edge.pstyle('width').pfValue;
21635 var scale = edge.pstyle('arrow-scale').value;
21636 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21637
21638 var widthSq = width * width;
21639 var width2 = width * 2;
21640 var src = _p.source;
21641 var tgt = _p.target;
21642 var sqDist;
21643
21644 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21645 var pts = rs.allpts;
21646
21647 for (var i = 0; i + 3 < pts.length; i += 2) {
21648 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]))) {
21649 addEle(edge, sqDist);
21650 return true;
21651 }
21652 }
21653 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21654 var pts = rs.allpts;
21655
21656 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21657 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]))) {
21658 addEle(edge, sqDist);
21659 return true;
21660 }
21661 }
21662 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
21663
21664
21665 var src = src || _p.source;
21666 var tgt = tgt || _p.target;
21667 var arSize = self.getArrowWidth(styleWidth, scale);
21668 var arrows = [{
21669 name: 'source',
21670 x: rs.arrowStartX,
21671 y: rs.arrowStartY,
21672 angle: rs.srcArrowAngle
21673 }, {
21674 name: 'target',
21675 x: rs.arrowEndX,
21676 y: rs.arrowEndY,
21677 angle: rs.tgtArrowAngle
21678 }, {
21679 name: 'mid-source',
21680 x: rs.midX,
21681 y: rs.midY,
21682 angle: rs.midsrcArrowAngle
21683 }, {
21684 name: 'mid-target',
21685 x: rs.midX,
21686 y: rs.midY,
21687 angle: rs.midtgtArrowAngle
21688 }];
21689
21690 for (var i = 0; i < arrows.length; i++) {
21691 var ar = arrows[i];
21692 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21693 var edgeWidth = edge.pstyle('width').pfValue;
21694
21695 if (shape.roughCollide(x, y, arSize, ar.angle, {
21696 x: ar.x,
21697 y: ar.y
21698 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21699 x: ar.x,
21700 y: ar.y
21701 }, edgeWidth, edgeThreshold)) {
21702 addEle(edge);
21703 return true;
21704 }
21705 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21706
21707
21708 if (hasCompounds && near.length > 0) {
21709 checkNode(src);
21710 checkNode(tgt);
21711 }
21712 }
21713
21714 function preprop(obj, name, pre) {
21715 return getPrefixedProperty(obj, name, pre);
21716 }
21717
21718 function checkLabel(ele, prefix) {
21719 var _p = ele._private;
21720 var th = labelThreshold;
21721 var prefixDash;
21722
21723 if (prefix) {
21724 prefixDash = prefix + '-';
21725 } else {
21726 prefixDash = '';
21727 }
21728
21729 ele.boundingBox();
21730 var bb = _p.labelBounds[prefix || 'main'];
21731 var text = ele.pstyle(prefixDash + 'label').value;
21732 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21733
21734 if (!eventsEnabled || !text) {
21735 return;
21736 }
21737
21738 var rstyle = _p.rstyle;
21739 var lx = preprop(rstyle, 'labelX', prefix);
21740 var ly = preprop(rstyle, 'labelY', prefix);
21741 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21742 var lx1 = bb.x1 - th;
21743 var lx2 = bb.x2 + th;
21744 var ly1 = bb.y1 - th;
21745 var ly2 = bb.y2 + th;
21746
21747 if (theta) {
21748 var cos = Math.cos(theta);
21749 var sin = Math.sin(theta);
21750
21751 var rotate = function rotate(x, y) {
21752 x = x - lx;
21753 y = y - ly;
21754 return {
21755 x: x * cos - y * sin + lx,
21756 y: x * sin + y * cos + ly
21757 };
21758 };
21759
21760 var px1y1 = rotate(lx1, ly1);
21761 var px1y2 = rotate(lx1, ly2);
21762 var px2y1 = rotate(lx2, ly1);
21763 var px2y2 = rotate(lx2, ly2);
21764 var points = [px1y1.x, px1y1.y, px2y1.x, px2y1.y, px2y2.x, px2y2.y, px1y2.x, px1y2.y];
21765
21766 if (pointInsidePolygonPoints(x, y, points)) {
21767 addEle(ele);
21768 return true;
21769 }
21770 } else {
21771 // do a cheaper bb check
21772 if (inBoundingBox(bb, x, y)) {
21773 addEle(ele);
21774 return true;
21775 }
21776 }
21777 }
21778
21779 for (var i = eles.length - 1; i >= 0; i--) {
21780 // reverse order for precedence
21781 var ele = eles[i];
21782
21783 if (ele.isNode()) {
21784 checkNode(ele) || checkLabel(ele);
21785 } else {
21786 // then edge
21787 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
21788 }
21789 }
21790
21791 return near;
21792}; // 'Give me everything from this box'
21793
21794
21795BRp$1.getAllInBox = function (x1, y1, x2, y2) {
21796 var eles = this.getCachedZSortedEles().interactive;
21797 var box = [];
21798 var x1c = Math.min(x1, x2);
21799 var x2c = Math.max(x1, x2);
21800 var y1c = Math.min(y1, y2);
21801 var y2c = Math.max(y1, y2);
21802 x1 = x1c;
21803 x2 = x2c;
21804 y1 = y1c;
21805 y2 = y2c;
21806 var boxBb = makeBoundingBox({
21807 x1: x1,
21808 y1: y1,
21809 x2: x2,
21810 y2: y2
21811 });
21812
21813 for (var e = 0; e < eles.length; e++) {
21814 var ele = eles[e];
21815
21816 if (ele.isNode()) {
21817 var node = ele;
21818 var nodeBb = node.boundingBox({
21819 includeNodes: true,
21820 includeEdges: false,
21821 includeLabels: false
21822 });
21823
21824 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
21825 box.push(node);
21826 }
21827 } else {
21828 var edge = ele;
21829 var _p = edge._private;
21830 var rs = _p.rscratch;
21831
21832 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
21833 continue;
21834 }
21835
21836 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
21837 continue;
21838 }
21839
21840 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
21841 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
21842 var allInside = true;
21843
21844 for (var i = 0; i < pts.length; i++) {
21845 if (!pointInBoundingBox(boxBb, pts[i])) {
21846 allInside = false;
21847 break;
21848 }
21849 }
21850
21851 if (allInside) {
21852 box.push(edge);
21853 }
21854 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
21855 box.push(edge);
21856 }
21857 }
21858 }
21859
21860 return box;
21861};
21862
21863var BRp$2 = {};
21864
21865BRp$2.calculateArrowAngles = function (edge) {
21866 var rs = edge._private.rscratch;
21867 var isHaystack = rs.edgeType === 'haystack';
21868 var isBezier = rs.edgeType === 'bezier';
21869 var isMultibezier = rs.edgeType === 'multibezier';
21870 var isSegments = rs.edgeType === 'segments';
21871 var isCompound = rs.edgeType === 'compound';
21872 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
21873
21874 var dispX, dispY;
21875 var startX, startY, endX, endY, midX, midY;
21876
21877 if (isHaystack) {
21878 startX = rs.haystackPts[0];
21879 startY = rs.haystackPts[1];
21880 endX = rs.haystackPts[2];
21881 endY = rs.haystackPts[3];
21882 } else {
21883 startX = rs.arrowStartX;
21884 startY = rs.arrowStartY;
21885 endX = rs.arrowEndX;
21886 endY = rs.arrowEndY;
21887 }
21888
21889 midX = rs.midX;
21890 midY = rs.midY; // source
21891 //
21892
21893 if (isSegments) {
21894 dispX = startX - rs.segpts[0];
21895 dispY = startY - rs.segpts[1];
21896 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21897 var pts = rs.allpts;
21898 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
21899 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
21900 dispX = startX - bX;
21901 dispY = startY - bY;
21902 } else {
21903 dispX = startX - midX;
21904 dispY = startY - midY;
21905 }
21906
21907 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
21908 //
21909
21910 var midX = rs.midX;
21911 var midY = rs.midY;
21912
21913 if (isHaystack) {
21914 midX = (startX + endX) / 2;
21915 midY = (startY + endY) / 2;
21916 }
21917
21918 dispX = endX - startX;
21919 dispY = endY - startY;
21920
21921 if (isSegments) {
21922 var pts = rs.allpts;
21923
21924 if (pts.length / 2 % 2 === 0) {
21925 var i2 = pts.length / 2;
21926 var i1 = i2 - 2;
21927 dispX = pts[i2] - pts[i1];
21928 dispY = pts[i2 + 1] - pts[i1 + 1];
21929 } else {
21930 var i2 = pts.length / 2 - 1;
21931 var i1 = i2 - 2;
21932 var i3 = i2 + 2;
21933 dispX = pts[i2] - pts[i1];
21934 dispY = pts[i2 + 1] - pts[i1 + 1];
21935 }
21936 } else if (isMultibezier || isCompound || isSelf) {
21937 var pts = rs.allpts;
21938 var cpts = rs.ctrlpts;
21939 var bp0x, bp0y;
21940 var bp1x, bp1y;
21941
21942 if (cpts.length / 2 % 2 === 0) {
21943 var p0 = pts.length / 2 - 1; // startpt
21944
21945 var ic = p0 + 2;
21946 var p1 = ic + 2;
21947 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
21948 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
21949 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
21950 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
21951 } else {
21952 var ic = pts.length / 2 - 1; // ctrpt
21953
21954 var p0 = ic - 2; // startpt
21955
21956 var p1 = ic + 2; // endpt
21957
21958 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
21959 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
21960 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
21961 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
21962 }
21963
21964 dispX = bp1x - bp0x;
21965 dispY = bp1y - bp0y;
21966 }
21967
21968 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
21969 rs.midDispX = dispX;
21970 rs.midDispY = dispY; // mid source
21971 //
21972
21973 dispX *= -1;
21974 dispY *= -1;
21975
21976 if (isSegments) {
21977 var pts = rs.allpts;
21978
21979 if (pts.length / 2 % 2 === 0) ; else {
21980 var i2 = pts.length / 2 - 1;
21981 var i3 = i2 + 2;
21982 dispX = -(pts[i3] - pts[i2]);
21983 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
21984 }
21985 }
21986
21987 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
21988 //
21989
21990 if (isSegments) {
21991 dispX = endX - rs.segpts[rs.segpts.length - 2];
21992 dispY = endY - rs.segpts[rs.segpts.length - 1];
21993 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21994 var pts = rs.allpts;
21995 var l = pts.length;
21996 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
21997 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
21998 dispX = endX - bX;
21999 dispY = endY - bY;
22000 } else {
22001 dispX = endX - midX;
22002 dispY = endY - midY;
22003 }
22004
22005 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22006};
22007
22008BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
22009 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22010 var cachedVal = cache[edgeWidth + ', ' + scale];
22011
22012 if (cachedVal) {
22013 return cachedVal;
22014 }
22015
22016 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22017 cache[edgeWidth + ', ' + scale] = cachedVal;
22018 return cachedVal;
22019};
22020
22021var BRp$3 = {};
22022
22023BRp$3.findHaystackPoints = function (edges) {
22024 for (var i = 0; i < edges.length; i++) {
22025 var edge = edges[i];
22026 var _p = edge._private;
22027 var rs = _p.rscratch;
22028
22029 if (!rs.haystack) {
22030 var angle = Math.random() * 2 * Math.PI;
22031 rs.source = {
22032 x: Math.cos(angle),
22033 y: Math.sin(angle)
22034 };
22035 angle = Math.random() * 2 * Math.PI;
22036 rs.target = {
22037 x: Math.cos(angle),
22038 y: Math.sin(angle)
22039 };
22040 }
22041
22042 var src = _p.source;
22043 var tgt = _p.target;
22044 var srcPos = src.position();
22045 var tgtPos = tgt.position();
22046 var srcW = src.width();
22047 var tgtW = tgt.width();
22048 var srcH = src.height();
22049 var tgtH = tgt.height();
22050 var radius = edge.pstyle('haystack-radius').value;
22051 var halfRadius = radius / 2; // b/c have to half width/height
22052
22053 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];
22054 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22055 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22056
22057 rs.edgeType = 'haystack';
22058 rs.haystack = true;
22059 this.storeEdgeProjections(edge);
22060 this.calculateArrowAngles(edge);
22061 this.recalculateEdgeLabelProjections(edge);
22062 this.calculateLabelAngles(edge);
22063 }
22064};
22065
22066BRp$3.findSegmentsPoints = function (edge, pairInfo) {
22067 // Segments (multiple straight lines)
22068 var rs = edge._private.rscratch;
22069 var posPts = pairInfo.posPts,
22070 intersectionPts = pairInfo.intersectionPts,
22071 vectorNormInverse = pairInfo.vectorNormInverse;
22072 var edgeDistances = edge.pstyle('edge-distances').value;
22073 var segmentWs = edge.pstyle('segment-weights');
22074 var segmentDs = edge.pstyle('segment-distances');
22075 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22076 rs.edgeType = 'segments';
22077 rs.segpts = [];
22078
22079 for (var s = 0; s < segmentsN; s++) {
22080 var w = segmentWs.pfValue[s];
22081 var d = segmentDs.pfValue[s];
22082 var w1 = 1 - w;
22083 var w2 = w;
22084 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22085 var adjustedMidpt = {
22086 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22087 y: midptPts.y1 * w1 + midptPts.y2 * w2
22088 };
22089 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22090 }
22091};
22092
22093BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22094 // Self-edge
22095 var rs = edge._private.rscratch;
22096 var dirCounts = pairInfo.dirCounts,
22097 srcPos = pairInfo.srcPos;
22098 var ctrlptDists = edge.pstyle('control-point-distances');
22099 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22100 var loopDir = edge.pstyle('loop-direction').pfValue;
22101 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22102 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22103 rs.edgeType = 'self';
22104 var j = i;
22105 var loopDist = stepSize;
22106
22107 if (edgeIsUnbundled) {
22108 j = 0;
22109 loopDist = ctrlptDist;
22110 }
22111
22112 var loopAngle = loopDir - Math.PI / 2;
22113 var outAngle = loopAngle - loopSwp / 2;
22114 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22115
22116 var dc = String(loopDir + '_' + loopSwp);
22117 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22118 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)];
22119};
22120
22121BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22122 // Compound edge
22123 var rs = edge._private.rscratch;
22124 rs.edgeType = 'compound';
22125 var srcPos = pairInfo.srcPos,
22126 tgtPos = pairInfo.tgtPos,
22127 srcW = pairInfo.srcW,
22128 srcH = pairInfo.srcH,
22129 tgtW = pairInfo.tgtW,
22130 tgtH = pairInfo.tgtH;
22131 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22132 var ctrlptDists = edge.pstyle('control-point-distances');
22133 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22134 var j = i;
22135 var loopDist = stepSize;
22136
22137 if (edgeIsUnbundled) {
22138 j = 0;
22139 loopDist = ctrlptDist;
22140 }
22141
22142 var loopW = 50;
22143 var loopaPos = {
22144 x: srcPos.x - srcW / 2,
22145 y: srcPos.y - srcH / 2
22146 };
22147 var loopbPos = {
22148 x: tgtPos.x - tgtW / 2,
22149 y: tgtPos.y - tgtH / 2
22150 };
22151 var loopPos = {
22152 x: Math.min(loopaPos.x, loopbPos.x),
22153 y: Math.min(loopaPos.y, loopbPos.y)
22154 }; // avoids cases with impossible beziers
22155
22156 var minCompoundStretch = 0.5;
22157 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22158 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22159 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];
22160};
22161
22162BRp$3.findStraightEdgePoints = function (edge) {
22163 // Straight edge within bundle
22164 edge._private.rscratch.edgeType = 'straight';
22165};
22166
22167BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22168 var rs = edge._private.rscratch;
22169 var vectorNormInverse = pairInfo.vectorNormInverse,
22170 posPts = pairInfo.posPts,
22171 intersectionPts = pairInfo.intersectionPts;
22172 var edgeDistances = edge.pstyle('edge-distances').value;
22173 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22174 var ctrlptDists = edge.pstyle('control-point-distances');
22175 var ctrlptWs = edge.pstyle('control-point-weights');
22176 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22177 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22178 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22179
22180 var multi = edgeIsUnbundled;
22181 rs.edgeType = multi ? 'multibezier' : 'bezier';
22182 rs.ctrlpts = [];
22183
22184 for (var b = 0; b < bezierN; b++) {
22185 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22186 var manctrlptDist = void 0;
22187 var sign = signum(normctrlptDist);
22188
22189 if (multi) {
22190 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22191
22192 ctrlptWeight = ctrlptWs.value[b];
22193 }
22194
22195 if (edgeIsUnbundled) {
22196 // multi or single unbundled
22197 manctrlptDist = ctrlptDist;
22198 } else {
22199 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22200 }
22201
22202 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22203 var w1 = 1 - ctrlptWeight;
22204 var w2 = ctrlptWeight;
22205 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22206 var adjustedMidpt = {
22207 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22208 y: midptPts.y1 * w1 + midptPts.y2 * w2
22209 };
22210 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22211 }
22212};
22213
22214BRp$3.findTaxiPoints = function (edge, pairInfo) {
22215 // Taxicab geometry with two turns maximum
22216 var rs = edge._private.rscratch;
22217 rs.edgeType = 'segments';
22218 var VERTICAL = 'vertical';
22219 var HORIZONTAL = 'horizontal';
22220 var LEFTWARD = 'leftward';
22221 var RIGHTWARD = 'rightward';
22222 var DOWNWARD = 'downward';
22223 var UPWARD = 'upward';
22224 var AUTO = 'auto';
22225 var posPts = pairInfo.posPts,
22226 srcW = pairInfo.srcW,
22227 srcH = pairInfo.srcH,
22228 tgtW = pairInfo.tgtW,
22229 tgtH = pairInfo.tgtH;
22230 var edgeDistances = edge.pstyle('edge-distances').value;
22231 var dIncludesNodeBody = edgeDistances !== 'node-position';
22232 var taxiDir = edge.pstyle('taxi-direction').value;
22233 var rawTaxiDir = taxiDir; // unprocessed value
22234
22235 var taxiTurn = edge.pstyle('taxi-turn');
22236 var turnIsPercent = taxiTurn.units === '%';
22237 var taxiTurnPfVal = taxiTurn.pfValue;
22238 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
22239
22240 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22241 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22242 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22243 var pdx = posPts.x2 - posPts.x1;
22244 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
22245
22246 var subDWH = function subDWH(dxy, dwh) {
22247 if (dxy > 0) {
22248 return Math.max(dxy - dwh, 0);
22249 } else {
22250 return Math.min(dxy + dwh, 0);
22251 }
22252 };
22253
22254 var dx = subDWH(pdx, dw);
22255 var dy = subDWH(pdy, dh);
22256 var isExplicitDir = false;
22257
22258 if (rawTaxiDir === AUTO) {
22259 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22260 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
22261 taxiDir = VERTICAL;
22262 isExplicitDir = true;
22263 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
22264 taxiDir = HORIZONTAL;
22265 isExplicitDir = true;
22266 }
22267
22268 var isVert = taxiDir === VERTICAL;
22269 var l = isVert ? dy : dx;
22270 var pl = isVert ? pdy : pdx;
22271 var sgnL = signum(pl);
22272 var forcedDir = false;
22273
22274 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
22275 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22276 sgnL *= -1;
22277 l = sgnL * Math.abs(l);
22278 forcedDir = true;
22279 }
22280
22281 var d;
22282
22283 if (turnIsPercent) {
22284 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
22285 d = p * l;
22286 } else {
22287 var k = taxiTurnPfVal < 0 ? l : 0;
22288 d = k + taxiTurnPfVal * sgnL;
22289 }
22290
22291 var getIsTooClose = function getIsTooClose(d) {
22292 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22293 };
22294
22295 var isTooCloseSrc = getIsTooClose(d);
22296 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
22297 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22298
22299 if (isTooClose && !forcedDir) {
22300 // non-ideal routing
22301 if (isVert) {
22302 // vertical fallbacks
22303 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22304 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22305
22306 if (lShapeInsideSrc) {
22307 // horizontal Z-shape (direction not respected)
22308 var x = (posPts.x1 + posPts.x2) / 2;
22309 var y1 = posPts.y1,
22310 y2 = posPts.y2;
22311 rs.segpts = [x, y1, x, y2];
22312 } else if (lShapeInsideTgt) {
22313 // vertical Z-shape (distance not respected)
22314 var y = (posPts.y1 + posPts.y2) / 2;
22315 var x1 = posPts.x1,
22316 x2 = posPts.x2;
22317 rs.segpts = [x1, y, x2, y];
22318 } else {
22319 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22320 rs.segpts = [posPts.x1, posPts.y2];
22321 }
22322 } else {
22323 // horizontal fallbacks
22324 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22325
22326 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22327
22328 if (_lShapeInsideSrc) {
22329 // vertical Z-shape (direction not respected)
22330 var _y = (posPts.y1 + posPts.y2) / 2;
22331
22332 var _x = posPts.x1,
22333 _x2 = posPts.x2;
22334 rs.segpts = [_x, _y, _x2, _y];
22335 } else if (_lShapeInsideTgt) {
22336 // horizontal Z-shape (turn distance not respected)
22337 var _x3 = (posPts.x1 + posPts.x2) / 2;
22338
22339 var _y2 = posPts.y1,
22340 _y3 = posPts.y2;
22341 rs.segpts = [_x3, _y2, _x3, _y3];
22342 } else {
22343 // L-shape (turn distance not respected, but works well for tree siblings)
22344 rs.segpts = [posPts.x2, posPts.y1];
22345 }
22346 }
22347 } else {
22348 // ideal routing
22349 if (isVert) {
22350 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22351
22352 var _x4 = posPts.x1,
22353 _x5 = posPts.x2;
22354 rs.segpts = [_x4, _y4, _x5, _y4];
22355 } else {
22356 // horizontal
22357 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22358
22359 var _y5 = posPts.y1,
22360 _y6 = posPts.y2;
22361 rs.segpts = [_x6, _y5, _x6, _y6];
22362 }
22363 }
22364};
22365
22366BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22367 var rs = edge._private.rscratch; // can only correct beziers for now...
22368
22369 if (rs.edgeType === 'bezier') {
22370 var srcPos = pairInfo.srcPos,
22371 tgtPos = pairInfo.tgtPos,
22372 srcW = pairInfo.srcW,
22373 srcH = pairInfo.srcH,
22374 tgtW = pairInfo.tgtW,
22375 tgtH = pairInfo.tgtH,
22376 srcShape = pairInfo.srcShape,
22377 tgtShape = pairInfo.tgtShape;
22378 var badStart = !number(rs.startX) || !number(rs.startY);
22379 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
22380 var badEnd = !number(rs.endX) || !number(rs.endY);
22381 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
22382 var minCpADistFactor = 3;
22383 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22384 var minCpADist = minCpADistFactor * arrowW;
22385 var startACpDist = dist({
22386 x: rs.ctrlpts[0],
22387 y: rs.ctrlpts[1]
22388 }, {
22389 x: rs.startX,
22390 y: rs.startY
22391 });
22392 var closeStartACp = startACpDist < minCpADist;
22393 var endACpDist = dist({
22394 x: rs.ctrlpts[0],
22395 y: rs.ctrlpts[1]
22396 }, {
22397 x: rs.endX,
22398 y: rs.endY
22399 });
22400 var closeEndACp = endACpDist < minCpADist;
22401 var overlapping = false;
22402
22403 if (badStart || badAStart || closeStartACp) {
22404 overlapping = true; // project control point along line from src centre to outside the src shape
22405 // (otherwise intersection will yield nothing)
22406
22407 var cpD = {
22408 // delta
22409 x: rs.ctrlpts[0] - srcPos.x,
22410 y: rs.ctrlpts[1] - srcPos.y
22411 };
22412 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22413
22414 var cpM = {
22415 // normalised delta
22416 x: cpD.x / cpL,
22417 y: cpD.y / cpL
22418 };
22419 var radius = Math.max(srcW, srcH);
22420 var cpProj = {
22421 // *2 radius guarantees outside shape
22422 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22423 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22424 };
22425 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
22426
22427 if (closeStartACp) {
22428 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22429 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22430 } else {
22431 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22432 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22433 }
22434 }
22435
22436 if (badEnd || badAEnd || closeEndACp) {
22437 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
22438 // (otherwise intersection will yield nothing)
22439
22440 var _cpD = {
22441 // delta
22442 x: rs.ctrlpts[0] - tgtPos.x,
22443 y: rs.ctrlpts[1] - tgtPos.y
22444 };
22445
22446 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22447
22448
22449 var _cpM = {
22450 // normalised delta
22451 x: _cpD.x / _cpL,
22452 y: _cpD.y / _cpL
22453 };
22454
22455 var _radius = Math.max(srcW, srcH);
22456
22457 var _cpProj = {
22458 // *2 radius guarantees outside shape
22459 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22460 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22461 };
22462 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
22463
22464 if (closeEndACp) {
22465 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22466 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22467 } else {
22468 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22469 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22470 }
22471 }
22472
22473 if (overlapping) {
22474 // recalc endpts
22475 this.findEndpoints(edge);
22476 }
22477 }
22478};
22479
22480BRp$3.storeAllpts = function (edge) {
22481 var rs = edge._private.rscratch;
22482
22483 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22484 rs.allpts = [];
22485 rs.allpts.push(rs.startX, rs.startY);
22486
22487 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22488 // ctrl pt itself
22489 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
22490
22491 if (b + 3 < rs.ctrlpts.length) {
22492 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22493 }
22494 }
22495
22496 rs.allpts.push(rs.endX, rs.endY);
22497 var m, mt;
22498
22499 if (rs.ctrlpts.length / 2 % 2 === 0) {
22500 m = rs.allpts.length / 2 - 1;
22501 rs.midX = rs.allpts[m];
22502 rs.midY = rs.allpts[m + 1];
22503 } else {
22504 m = rs.allpts.length / 2 - 3;
22505 mt = 0.5;
22506 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22507 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22508 }
22509 } else if (rs.edgeType === 'straight') {
22510 // need to calc these after endpts
22511 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
22512
22513 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22514 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22515 } else if (rs.edgeType === 'segments') {
22516 rs.allpts = [];
22517 rs.allpts.push(rs.startX, rs.startY);
22518 rs.allpts.push.apply(rs.allpts, rs.segpts);
22519 rs.allpts.push(rs.endX, rs.endY);
22520
22521 if (rs.segpts.length % 4 === 0) {
22522 var i2 = rs.segpts.length / 2;
22523 var i1 = i2 - 2;
22524 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22525 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22526 } else {
22527 var _i = rs.segpts.length / 2 - 1;
22528
22529 rs.midX = rs.segpts[_i];
22530 rs.midY = rs.segpts[_i + 1];
22531 }
22532 }
22533};
22534
22535BRp$3.checkForInvalidEdgeWarning = function (edge) {
22536 var rs = edge[0]._private.rscratch;
22537
22538 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
22539 rs.loggedErr = false;
22540 } else {
22541 if (!rs.loggedErr) {
22542 rs.loggedErr = true;
22543 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.');
22544 }
22545 }
22546};
22547
22548BRp$3.findEdgeControlPoints = function (edges) {
22549 var _this = this;
22550
22551 if (!edges || edges.length === 0) {
22552 return;
22553 }
22554
22555 var r = this;
22556 var cy = r.cy;
22557 var hasCompounds = cy.hasCompoundNodes();
22558 var hashTable = {
22559 map: new Map$1(),
22560 get: function get(pairId) {
22561 var map2 = this.map.get(pairId[0]);
22562
22563 if (map2 != null) {
22564 return map2.get(pairId[1]);
22565 } else {
22566 return null;
22567 }
22568 },
22569 set: function set(pairId, val) {
22570 var map2 = this.map.get(pairId[0]);
22571
22572 if (map2 == null) {
22573 map2 = new Map$1();
22574 this.map.set(pairId[0], map2);
22575 }
22576
22577 map2.set(pairId[1], val);
22578 }
22579 };
22580 var pairIds = [];
22581 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
22582
22583 for (var i = 0; i < edges.length; i++) {
22584 var edge = edges[i];
22585 var _p = edge._private;
22586 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
22587 // they shouldn't take up space
22588
22589 if (edge.removed() || !edge.takesUpSpace()) {
22590 continue;
22591 }
22592
22593 if (curveStyle === 'haystack') {
22594 haystackEdges.push(edge);
22595 continue;
22596 }
22597
22598 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
22599 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
22600 var src = _p.source;
22601 var tgt = _p.target;
22602 var srcIndex = src.poolIndex();
22603 var tgtIndex = tgt.poolIndex();
22604 var pairId = [srcIndex, tgtIndex].sort();
22605 var tableEntry = hashTable.get(pairId);
22606
22607 if (tableEntry == null) {
22608 tableEntry = {
22609 eles: []
22610 };
22611 hashTable.set(pairId, tableEntry);
22612 pairIds.push(pairId);
22613 }
22614
22615 tableEntry.eles.push(edge);
22616
22617 if (edgeIsUnbundled) {
22618 tableEntry.hasUnbundled = true;
22619 }
22620
22621 if (edgeIsBezier) {
22622 tableEntry.hasBezier = true;
22623 }
22624 } // for each pair (src, tgt), create the ctrl pts
22625 // Nested for loop is OK; total number of iterations for both loops = edgeCount
22626
22627
22628 var _loop = function _loop(p) {
22629 var pairId = pairIds[p];
22630 var pairInfo = hashTable.get(pairId);
22631 var swappedpairInfo = void 0;
22632
22633 if (!pairInfo.hasUnbundled) {
22634 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
22635 return e.isBundledBezier();
22636 });
22637 clearArray(pairInfo.eles);
22638 pllEdges.forEach(function (edge) {
22639 return pairInfo.eles.push(edge);
22640 }); // for each pair id, the edges should be sorted by index
22641
22642 pairInfo.eles.sort(function (edge1, edge2) {
22643 return edge1.poolIndex() - edge2.poolIndex();
22644 });
22645 }
22646
22647 var firstEdge = pairInfo.eles[0];
22648 var src = firstEdge.source();
22649 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
22650
22651 if (src.poolIndex() > tgt.poolIndex()) {
22652 var temp = src;
22653 src = tgt;
22654 tgt = temp;
22655 }
22656
22657 var srcPos = pairInfo.srcPos = src.position();
22658 var tgtPos = pairInfo.tgtPos = tgt.position();
22659 var srcW = pairInfo.srcW = src.outerWidth();
22660 var srcH = pairInfo.srcH = src.outerHeight();
22661 var tgtW = pairInfo.tgtW = tgt.outerWidth();
22662 var tgtH = pairInfo.tgtH = tgt.outerHeight();
22663
22664 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
22665
22666 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
22667
22668 pairInfo.dirCounts = {
22669 'north': 0,
22670 'west': 0,
22671 'south': 0,
22672 'east': 0,
22673 'northwest': 0,
22674 'southwest': 0,
22675 'northeast': 0,
22676 'southeast': 0
22677 };
22678
22679 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
22680 var _edge = pairInfo.eles[_i2];
22681 var rs = _edge[0]._private.rscratch;
22682
22683 var _curveStyle = _edge.pstyle('curve-style').value;
22684
22685 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
22686
22687
22688 var edgeIsSwapped = !src.same(_edge.source());
22689
22690 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
22691 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
22692
22693 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
22694 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
22695
22696 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
22697 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
22698 var intersectionPts = pairInfo.intersectionPts = {
22699 x1: srcOutside[0],
22700 x2: tgtOutside[0],
22701 y1: srcOutside[1],
22702 y2: tgtOutside[1]
22703 };
22704 var posPts = pairInfo.posPts = {
22705 x1: srcPos.x,
22706 x2: tgtPos.x,
22707 y1: srcPos.y,
22708 y2: tgtPos.y
22709 };
22710 var dy = tgtOutside[1] - srcOutside[1];
22711 var dx = tgtOutside[0] - srcOutside[0];
22712 var l = Math.sqrt(dx * dx + dy * dy);
22713 var vector = pairInfo.vector = {
22714 x: dx,
22715 y: dy
22716 };
22717 var vectorNorm = pairInfo.vectorNorm = {
22718 x: vector.x / l,
22719 y: vector.y / l
22720 };
22721 var vectorNormInverse = {
22722 x: -vectorNorm.y,
22723 y: vectorNorm.x
22724 }; // if node shapes overlap, then no ctrl pts to draw
22725
22726 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);
22727 pairInfo.vectorNormInverse = vectorNormInverse;
22728 swappedpairInfo = {
22729 nodesOverlap: pairInfo.nodesOverlap,
22730 dirCounts: pairInfo.dirCounts,
22731 calculatedIntersection: true,
22732 hasBezier: pairInfo.hasBezier,
22733 hasUnbundled: pairInfo.hasUnbundled,
22734 eles: pairInfo.eles,
22735 srcPos: tgtPos,
22736 tgtPos: srcPos,
22737 srcW: tgtW,
22738 srcH: tgtH,
22739 tgtW: srcW,
22740 tgtH: srcH,
22741 srcIntn: tgtIntn,
22742 tgtIntn: srcIntn,
22743 srcShape: tgtShape,
22744 tgtShape: srcShape,
22745 posPts: {
22746 x1: posPts.x2,
22747 y1: posPts.y2,
22748 x2: posPts.x1,
22749 y2: posPts.y1
22750 },
22751 intersectionPts: {
22752 x1: intersectionPts.x2,
22753 y1: intersectionPts.y2,
22754 x2: intersectionPts.x1,
22755 y2: intersectionPts.y1
22756 },
22757 vector: {
22758 x: -vector.x,
22759 y: -vector.y
22760 },
22761 vectorNorm: {
22762 x: -vectorNorm.x,
22763 y: -vectorNorm.y
22764 },
22765 vectorNormInverse: {
22766 x: -vectorNormInverse.x,
22767 y: -vectorNormInverse.y
22768 }
22769 };
22770 }
22771
22772 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
22773 rs.nodesOverlap = passedPairInfo.nodesOverlap;
22774 rs.srcIntn = passedPairInfo.srcIntn;
22775 rs.tgtIntn = passedPairInfo.tgtIntn;
22776
22777 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
22778 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22779 } else if (src === tgt) {
22780 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22781 } else if (_curveStyle === 'segments') {
22782 _this.findSegmentsPoints(_edge, passedPairInfo);
22783 } else if (_curveStyle === 'taxi') {
22784 _this.findTaxiPoints(_edge, passedPairInfo);
22785 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
22786 _this.findStraightEdgePoints(_edge);
22787 } else {
22788 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
22789 }
22790
22791 _this.findEndpoints(_edge);
22792
22793 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
22794
22795 _this.checkForInvalidEdgeWarning(_edge);
22796
22797 _this.storeAllpts(_edge);
22798
22799 _this.storeEdgeProjections(_edge);
22800
22801 _this.calculateArrowAngles(_edge);
22802
22803 _this.recalculateEdgeLabelProjections(_edge);
22804
22805 _this.calculateLabelAngles(_edge);
22806 } // for pair edges
22807
22808 };
22809
22810 for (var p = 0; p < pairIds.length; p++) {
22811 _loop(p);
22812 } // for pair ids
22813 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
22814
22815
22816 this.findHaystackPoints(haystackEdges);
22817};
22818
22819function getPts(pts) {
22820 var retPts = [];
22821
22822 if (pts == null) {
22823 return;
22824 }
22825
22826 for (var i = 0; i < pts.length; i += 2) {
22827 var x = pts[i];
22828 var y = pts[i + 1];
22829 retPts.push({
22830 x: x,
22831 y: y
22832 });
22833 }
22834
22835 return retPts;
22836}
22837
22838BRp$3.getSegmentPoints = function (edge) {
22839 var rs = edge[0]._private.rscratch;
22840 var type = rs.edgeType;
22841
22842 if (type === 'segments') {
22843 this.recalculateRenderedStyle(edge);
22844 return getPts(rs.segpts);
22845 }
22846};
22847
22848BRp$3.getControlPoints = function (edge) {
22849 var rs = edge[0]._private.rscratch;
22850 var type = rs.edgeType;
22851
22852 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
22853 this.recalculateRenderedStyle(edge);
22854 return getPts(rs.ctrlpts);
22855 }
22856};
22857
22858BRp$3.getEdgeMidpoint = function (edge) {
22859 var rs = edge[0]._private.rscratch;
22860 this.recalculateRenderedStyle(edge);
22861 return {
22862 x: rs.midX,
22863 y: rs.midY
22864 };
22865};
22866
22867var BRp$4 = {};
22868
22869BRp$4.manualEndptToPx = function (node, prop) {
22870 var r = this;
22871 var npos = node.position();
22872 var w = node.outerWidth();
22873 var h = node.outerHeight();
22874
22875 if (prop.value.length === 2) {
22876 var p = [prop.pfValue[0], prop.pfValue[1]];
22877
22878 if (prop.units[0] === '%') {
22879 p[0] = p[0] * w;
22880 }
22881
22882 if (prop.units[1] === '%') {
22883 p[1] = p[1] * h;
22884 }
22885
22886 p[0] += npos.x;
22887 p[1] += npos.y;
22888 return p;
22889 } else {
22890 var angle = prop.pfValue[0];
22891 angle = -Math.PI / 2 + angle; // start at 12 o'clock
22892
22893 var l = 2 * Math.max(w, h);
22894 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
22895 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
22896 }
22897};
22898
22899BRp$4.findEndpoints = function (edge) {
22900 var r = this;
22901 var intersect;
22902 var source = edge.source()[0];
22903 var target = edge.target()[0];
22904 var srcPos = source.position();
22905 var tgtPos = target.position();
22906 var tgtArShape = edge.pstyle('target-arrow-shape').value;
22907 var srcArShape = edge.pstyle('source-arrow-shape').value;
22908 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
22909 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
22910 var curveStyle = edge.pstyle('curve-style').value;
22911 var rs = edge._private.rscratch;
22912 var et = rs.edgeType;
22913 var taxi = curveStyle === 'taxi';
22914 var self = et === 'self' || et === 'compound';
22915 var bezier = et === 'bezier' || et === 'multibezier' || self;
22916 var multi = et !== 'bezier';
22917 var lines = et === 'straight' || et === 'segments';
22918 var segments = et === 'segments';
22919 var hasEndpts = bezier || multi || lines;
22920 var overrideEndpts = self || taxi;
22921 var srcManEndpt = edge.pstyle('source-endpoint');
22922 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
22923 var tgtManEndpt = edge.pstyle('target-endpoint');
22924 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
22925 rs.srcManEndpt = srcManEndpt;
22926 rs.tgtManEndpt = tgtManEndpt;
22927 var p1; // last known point of edge on target side
22928
22929 var p2; // last known point of edge on source side
22930
22931 var p1_i; // point to intersect with target shape
22932
22933 var p2_i; // point to intersect with source shape
22934
22935 if (bezier) {
22936 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
22937 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
22938 p1 = cpEnd;
22939 p2 = cpStart;
22940 } else if (lines) {
22941 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
22942 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
22943 p1 = tgtArrowFromPt;
22944 p2 = srcArrowFromPt;
22945 }
22946
22947 if (tgtManEndptVal === 'inside-to-node') {
22948 intersect = [tgtPos.x, tgtPos.y];
22949 } else if (tgtManEndpt.units) {
22950 intersect = this.manualEndptToPx(target, tgtManEndpt);
22951 } else if (tgtManEndptVal === 'outside-to-line') {
22952 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
22953 } else {
22954 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
22955 p1_i = p1;
22956 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
22957 p1_i = [srcPos.x, srcPos.y];
22958 }
22959
22960 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
22961
22962 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
22963 var trs = target._private.rscratch;
22964 var lw = trs.labelWidth;
22965 var lh = trs.labelHeight;
22966 var lx = trs.labelX;
22967 var ly = trs.labelY;
22968 var lw2 = lw / 2;
22969 var lh2 = lh / 2;
22970 var va = target.pstyle('text-valign').value;
22971
22972 if (va === 'top') {
22973 ly -= lh2;
22974 } else if (va === 'bottom') {
22975 ly += lh2;
22976 }
22977
22978 var ha = target.pstyle('text-halign').value;
22979
22980 if (ha === 'left') {
22981 lx -= lw2;
22982 } else if (ha === 'right') {
22983 lx += lw2;
22984 }
22985
22986 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);
22987
22988 if (labelIntersect.length > 0) {
22989 var refPt = srcPos;
22990 var intSqdist = sqdist(refPt, array2point(intersect));
22991 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
22992 var minSqDist = intSqdist;
22993
22994 if (labIntSqdist < intSqdist) {
22995 intersect = labelIntersect;
22996 minSqDist = labIntSqdist;
22997 }
22998
22999 if (labelIntersect.length > 2) {
23000 var labInt2SqDist = sqdist(refPt, {
23001 x: labelIntersect[2],
23002 y: labelIntersect[3]
23003 });
23004
23005 if (labInt2SqDist < minSqDist) {
23006 intersect = [labelIntersect[2], labelIntersect[3]];
23007 }
23008 }
23009 }
23010 }
23011 }
23012
23013 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23014 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23015 rs.endX = edgeEnd[0];
23016 rs.endY = edgeEnd[1];
23017 rs.arrowEndX = arrowEnd[0];
23018 rs.arrowEndY = arrowEnd[1];
23019
23020 if (srcManEndptVal === 'inside-to-node') {
23021 intersect = [srcPos.x, srcPos.y];
23022 } else if (srcManEndpt.units) {
23023 intersect = this.manualEndptToPx(source, srcManEndpt);
23024 } else if (srcManEndptVal === 'outside-to-line') {
23025 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23026 } else {
23027 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23028 p2_i = p2;
23029 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23030 p2_i = [tgtPos.x, tgtPos.y];
23031 }
23032
23033 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23034
23035 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23036 var srs = source._private.rscratch;
23037 var _lw = srs.labelWidth;
23038 var _lh = srs.labelHeight;
23039 var _lx = srs.labelX;
23040 var _ly = srs.labelY;
23041
23042 var _lw2 = _lw / 2;
23043
23044 var _lh2 = _lh / 2;
23045
23046 var _va = source.pstyle('text-valign').value;
23047
23048 if (_va === 'top') {
23049 _ly -= _lh2;
23050 } else if (_va === 'bottom') {
23051 _ly += _lh2;
23052 }
23053
23054 var _ha = source.pstyle('text-halign').value;
23055
23056 if (_ha === 'left') {
23057 _lx -= _lw2;
23058 } else if (_ha === 'right') {
23059 _lx += _lw2;
23060 }
23061
23062 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);
23063
23064 if (_labelIntersect.length > 0) {
23065 var _refPt = tgtPos;
23066
23067 var _intSqdist = sqdist(_refPt, array2point(intersect));
23068
23069 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23070
23071 var _minSqDist = _intSqdist;
23072
23073 if (_labIntSqdist < _intSqdist) {
23074 intersect = [_labelIntersect[0], _labelIntersect[1]];
23075 _minSqDist = _labIntSqdist;
23076 }
23077
23078 if (_labelIntersect.length > 2) {
23079 var _labInt2SqDist = sqdist(_refPt, {
23080 x: _labelIntersect[2],
23081 y: _labelIntersect[3]
23082 });
23083
23084 if (_labInt2SqDist < _minSqDist) {
23085 intersect = [_labelIntersect[2], _labelIntersect[3]];
23086 }
23087 }
23088 }
23089 }
23090 }
23091
23092 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23093 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23094 rs.startX = edgeStart[0];
23095 rs.startY = edgeStart[1];
23096 rs.arrowStartX = arrowStart[0];
23097 rs.arrowStartY = arrowStart[1];
23098
23099 if (hasEndpts) {
23100 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
23101 rs.badLine = true;
23102 } else {
23103 rs.badLine = false;
23104 }
23105 }
23106};
23107
23108BRp$4.getSourceEndpoint = function (edge) {
23109 var rs = edge[0]._private.rscratch;
23110 this.recalculateRenderedStyle(edge);
23111
23112 switch (rs.edgeType) {
23113 case 'haystack':
23114 return {
23115 x: rs.haystackPts[0],
23116 y: rs.haystackPts[1]
23117 };
23118
23119 default:
23120 return {
23121 x: rs.arrowStartX,
23122 y: rs.arrowStartY
23123 };
23124 }
23125};
23126
23127BRp$4.getTargetEndpoint = function (edge) {
23128 var rs = edge[0]._private.rscratch;
23129 this.recalculateRenderedStyle(edge);
23130
23131 switch (rs.edgeType) {
23132 case 'haystack':
23133 return {
23134 x: rs.haystackPts[2],
23135 y: rs.haystackPts[3]
23136 };
23137
23138 default:
23139 return {
23140 x: rs.arrowEndX,
23141 y: rs.arrowEndY
23142 };
23143 }
23144};
23145
23146var BRp$5 = {};
23147
23148function pushBezierPts(r, edge, pts) {
23149 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23150 return qbezierAt(p1, p2, p3, t);
23151 };
23152
23153 var _p = edge._private;
23154 var bpts = _p.rstyle.bezierPts;
23155
23156 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23157 var p = r.bezierProjPcts[i];
23158 bpts.push({
23159 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23160 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23161 });
23162 }
23163}
23164
23165BRp$5.storeEdgeProjections = function (edge) {
23166 var _p = edge._private;
23167 var rs = _p.rscratch;
23168 var et = rs.edgeType; // clear the cached points state
23169
23170 _p.rstyle.bezierPts = null;
23171 _p.rstyle.linePts = null;
23172 _p.rstyle.haystackPts = null;
23173
23174 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23175 _p.rstyle.bezierPts = [];
23176
23177 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23178 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23179 }
23180 } else if (et === 'segments') {
23181 var lpts = _p.rstyle.linePts = [];
23182
23183 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23184 lpts.push({
23185 x: rs.allpts[i],
23186 y: rs.allpts[i + 1]
23187 });
23188 }
23189 } else if (et === 'haystack') {
23190 var hpts = rs.haystackPts;
23191 _p.rstyle.haystackPts = [{
23192 x: hpts[0],
23193 y: hpts[1]
23194 }, {
23195 x: hpts[2],
23196 y: hpts[3]
23197 }];
23198 }
23199
23200 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23201};
23202
23203BRp$5.recalculateEdgeProjections = function (edges) {
23204 this.findEdgeControlPoints(edges);
23205};
23206
23207/* global document */
23208
23209var BRp$6 = {};
23210
23211BRp$6.recalculateNodeLabelProjection = function (node) {
23212 var content = node.pstyle('label').strValue;
23213
23214 if (emptyString(content)) {
23215 return;
23216 }
23217
23218 var textX, textY;
23219 var _p = node._private;
23220 var nodeWidth = node.width();
23221 var nodeHeight = node.height();
23222 var padding = node.padding();
23223 var nodePos = node.position();
23224 var textHalign = node.pstyle('text-halign').strValue;
23225 var textValign = node.pstyle('text-valign').strValue;
23226 var rs = _p.rscratch;
23227 var rstyle = _p.rstyle;
23228
23229 switch (textHalign) {
23230 case 'left':
23231 textX = nodePos.x - nodeWidth / 2 - padding;
23232 break;
23233
23234 case 'right':
23235 textX = nodePos.x + nodeWidth / 2 + padding;
23236 break;
23237
23238 default:
23239 // e.g. center
23240 textX = nodePos.x;
23241 }
23242
23243 switch (textValign) {
23244 case 'top':
23245 textY = nodePos.y - nodeHeight / 2 - padding;
23246 break;
23247
23248 case 'bottom':
23249 textY = nodePos.y + nodeHeight / 2 + padding;
23250 break;
23251
23252 default:
23253 // e.g. middle
23254 textY = nodePos.y;
23255 }
23256
23257 rs.labelX = textX;
23258 rs.labelY = textY;
23259 rstyle.labelX = textX;
23260 rstyle.labelY = textY;
23261 this.applyLabelDimensions(node);
23262};
23263
23264var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23265 var angle = Math.atan(dy / dx);
23266
23267 if (dx === 0 && angle < 0) {
23268 angle = angle * -1;
23269 }
23270
23271 return angle;
23272};
23273
23274var lineAngle = function lineAngle(p0, p1) {
23275 var dx = p1.x - p0.x;
23276 var dy = p1.y - p0.y;
23277 return lineAngleFromDelta(dx, dy);
23278};
23279
23280var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23281 var t0 = bound(0, t - 0.001, 1);
23282 var t1 = bound(0, t + 0.001, 1);
23283 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23284 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23285 return lineAngle(lp0, lp1);
23286};
23287
23288BRp$6.recalculateEdgeLabelProjections = function (edge) {
23289 var p;
23290 var _p = edge._private;
23291 var rs = _p.rscratch;
23292 var r = this;
23293 var content = {
23294 mid: edge.pstyle('label').strValue,
23295 source: edge.pstyle('source-label').strValue,
23296 target: edge.pstyle('target-label').strValue
23297 };
23298
23299 if (content.mid || content.source || content.target) ; else {
23300 return; // no labels => no calcs
23301 } // add center point to style so bounding box calculations can use it
23302 //
23303
23304
23305 p = {
23306 x: rs.midX,
23307 y: rs.midY
23308 };
23309
23310 var setRs = function setRs(propName, prefix, value) {
23311 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23312 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23313 };
23314
23315 setRs('labelX', null, p.x);
23316 setRs('labelY', null, p.y);
23317 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23318 setRs('labelAutoAngle', null, midAngle);
23319
23320 var createControlPointInfo = function createControlPointInfo() {
23321 if (createControlPointInfo.cache) {
23322 return createControlPointInfo.cache;
23323 } // use cache so only 1x per edge
23324
23325
23326 var ctrlpts = []; // store each ctrlpt info init
23327
23328 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23329 var p0 = {
23330 x: rs.allpts[i],
23331 y: rs.allpts[i + 1]
23332 };
23333 var p1 = {
23334 x: rs.allpts[i + 2],
23335 y: rs.allpts[i + 3]
23336 }; // ctrlpt
23337
23338 var p2 = {
23339 x: rs.allpts[i + 4],
23340 y: rs.allpts[i + 5]
23341 };
23342 ctrlpts.push({
23343 p0: p0,
23344 p1: p1,
23345 p2: p2,
23346 startDist: 0,
23347 length: 0,
23348 segments: []
23349 });
23350 }
23351
23352 var bpts = _p.rstyle.bezierPts;
23353 var nProjs = r.bezierProjPcts.length;
23354
23355 function addSegment(cp, p0, p1, t0, t1) {
23356 var length = dist(p0, p1);
23357 var prevSegment = cp.segments[cp.segments.length - 1];
23358 var segment = {
23359 p0: p0,
23360 p1: p1,
23361 t0: t0,
23362 t1: t1,
23363 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23364 length: length
23365 };
23366 cp.segments.push(segment);
23367 cp.length += length;
23368 } // update each ctrlpt with segment info
23369
23370
23371 for (var _i = 0; _i < ctrlpts.length; _i++) {
23372 var cp = ctrlpts[_i];
23373 var prevCp = ctrlpts[_i - 1];
23374
23375 if (prevCp) {
23376 cp.startDist = prevCp.startDist + prevCp.length;
23377 }
23378
23379 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23380
23381 for (var j = 0; j < nProjs - 1; j++) {
23382 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23383 }
23384
23385 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23386 }
23387
23388 return createControlPointInfo.cache = ctrlpts;
23389 };
23390
23391 var calculateEndProjection = function calculateEndProjection(prefix) {
23392 var angle;
23393 var isSrc = prefix === 'source';
23394
23395 if (!content[prefix]) {
23396 return;
23397 }
23398
23399 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23400
23401 switch (rs.edgeType) {
23402 case 'self':
23403 case 'compound':
23404 case 'bezier':
23405 case 'multibezier':
23406 {
23407 var cps = createControlPointInfo();
23408 var selected;
23409 var startDist = 0;
23410 var totalDist = 0; // find the segment we're on
23411
23412 for (var i = 0; i < cps.length; i++) {
23413 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23414
23415 for (var j = 0; j < _cp.segments.length; j++) {
23416 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23417 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23418 startDist = totalDist;
23419 totalDist += _seg.length;
23420
23421 if (totalDist >= offset || lastSeg) {
23422 selected = {
23423 cp: _cp,
23424 segment: _seg
23425 };
23426 break;
23427 }
23428 }
23429
23430 if (selected) {
23431 break;
23432 }
23433 }
23434
23435 var cp = selected.cp;
23436 var seg = selected.segment;
23437 var tSegment = (offset - startDist) / seg.length;
23438 var segDt = seg.t1 - seg.t0;
23439 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23440 t = bound(0, t, 1);
23441 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23442 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23443 break;
23444 }
23445
23446 case 'straight':
23447 case 'segments':
23448 case 'haystack':
23449 {
23450 var d = 0,
23451 di,
23452 d0;
23453 var p0, p1;
23454 var l = rs.allpts.length;
23455
23456 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23457 if (isSrc) {
23458 p0 = {
23459 x: rs.allpts[_i2],
23460 y: rs.allpts[_i2 + 1]
23461 };
23462 p1 = {
23463 x: rs.allpts[_i2 + 2],
23464 y: rs.allpts[_i2 + 3]
23465 };
23466 } else {
23467 p0 = {
23468 x: rs.allpts[l - 2 - _i2],
23469 y: rs.allpts[l - 1 - _i2]
23470 };
23471 p1 = {
23472 x: rs.allpts[l - 4 - _i2],
23473 y: rs.allpts[l - 3 - _i2]
23474 };
23475 }
23476
23477 di = dist(p0, p1);
23478 d0 = d;
23479 d += di;
23480
23481 if (d >= offset) {
23482 break;
23483 }
23484 }
23485
23486 var pD = offset - d0;
23487
23488 var _t = pD / di;
23489
23490 _t = bound(0, _t, 1);
23491 p = lineAt(p0, p1, _t);
23492 angle = lineAngle(p0, p1);
23493 break;
23494 }
23495 }
23496
23497 setRs('labelX', prefix, p.x);
23498 setRs('labelY', prefix, p.y);
23499 setRs('labelAutoAngle', prefix, angle);
23500 };
23501
23502 calculateEndProjection('source');
23503 calculateEndProjection('target');
23504 this.applyLabelDimensions(edge);
23505};
23506
23507BRp$6.applyLabelDimensions = function (ele) {
23508 this.applyPrefixedLabelDimensions(ele);
23509
23510 if (ele.isEdge()) {
23511 this.applyPrefixedLabelDimensions(ele, 'source');
23512 this.applyPrefixedLabelDimensions(ele, 'target');
23513 }
23514};
23515
23516BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
23517 var _p = ele._private;
23518 var text = this.getLabelText(ele, prefix);
23519 var labelDims = this.calculateLabelDimensions(ele, text);
23520 var lineHeight = ele.pstyle('line-height').pfValue;
23521 var textWrap = ele.pstyle('text-wrap').strValue;
23522 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23523 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23524 var normPerLineHeight = labelDims.height / numLines;
23525 var labelLineHeight = normPerLineHeight * lineHeight;
23526 var width = labelDims.width;
23527 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23528 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23529 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23530 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23531 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23532 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23533};
23534
23535BRp$6.getLabelText = function (ele, prefix) {
23536 var _p = ele._private;
23537 var pfd = prefix ? prefix + '-' : '';
23538 var text = ele.pstyle(pfd + 'label').strValue;
23539 var textTransform = ele.pstyle('text-transform').value;
23540
23541 var rscratch = function rscratch(propName, value) {
23542 if (value) {
23543 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23544 return value;
23545 } else {
23546 return getPrefixedProperty(_p.rscratch, propName, prefix);
23547 }
23548 }; // for empty text, skip all processing
23549
23550
23551 if (!text) {
23552 return '';
23553 }
23554
23555 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23556 text = text.toUpperCase();
23557 } else if (textTransform == 'lowercase') {
23558 text = text.toLowerCase();
23559 }
23560
23561 var wrapStyle = ele.pstyle('text-wrap').value;
23562
23563 if (wrapStyle === 'wrap') {
23564 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
23565
23566 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23567 return rscratch('labelWrapCachedText');
23568 }
23569
23570 var zwsp = "\u200B";
23571 var lines = text.split('\n');
23572 var maxW = ele.pstyle('text-max-width').pfValue;
23573 var overflow = ele.pstyle('text-overflow-wrap').value;
23574 var overflowAny = overflow === 'anywhere';
23575 var wrappedLines = [];
23576 var wordsRegex = /[\s\u200b]+/;
23577 var wordSeparator = overflowAny ? '' : ' ';
23578
23579 for (var l = 0; l < lines.length; l++) {
23580 var line = lines[l];
23581 var lineDims = this.calculateLabelDimensions(ele, line);
23582 var lineW = lineDims.width;
23583
23584 if (overflowAny) {
23585 var processedLine = line.split('').join(zwsp);
23586 line = processedLine;
23587 }
23588
23589 if (lineW > maxW) {
23590 // line is too long
23591 var words = line.split(wordsRegex);
23592 var subline = '';
23593
23594 for (var w = 0; w < words.length; w++) {
23595 var word = words[w];
23596 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
23597 var testDims = this.calculateLabelDimensions(ele, testLine);
23598 var testW = testDims.width;
23599
23600 if (testW <= maxW) {
23601 // word fits on current line
23602 subline += word + wordSeparator;
23603 } else {
23604 // word starts new line
23605 if (subline) {
23606 wrappedLines.push(subline);
23607 }
23608
23609 subline = word + wordSeparator;
23610 }
23611 } // if there's remaining text, put it in a wrapped line
23612
23613
23614 if (!subline.match(/^[\s\u200b]+$/)) {
23615 wrappedLines.push(subline);
23616 }
23617 } else {
23618 // line is already short enough
23619 wrappedLines.push(line);
23620 }
23621 } // for
23622
23623
23624 rscratch('labelWrapCachedLines', wrappedLines);
23625 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23626 rscratch('labelWrapKey', labelKey);
23627 } else if (wrapStyle === 'ellipsis') {
23628 var _maxW = ele.pstyle('text-max-width').pfValue;
23629 var ellipsized = '';
23630 var ellipsis = "\u2026";
23631 var incLastCh = false;
23632
23633 for (var i = 0; i < text.length; i++) {
23634 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23635
23636 if (widthWithNextCh > _maxW) {
23637 break;
23638 }
23639
23640 ellipsized += text[i];
23641
23642 if (i === text.length - 1) {
23643 incLastCh = true;
23644 }
23645 }
23646
23647 if (!incLastCh) {
23648 ellipsized += ellipsis;
23649 }
23650
23651 return ellipsized;
23652 } // if ellipsize
23653
23654
23655 return text;
23656};
23657
23658BRp$6.getLabelJustification = function (ele) {
23659 var justification = ele.pstyle('text-justification').strValue;
23660 var textHalign = ele.pstyle('text-halign').strValue;
23661
23662 if (justification === 'auto') {
23663 if (ele.isNode()) {
23664 switch (textHalign) {
23665 case 'left':
23666 return 'right';
23667
23668 case 'right':
23669 return 'left';
23670
23671 default:
23672 return 'center';
23673 }
23674 } else {
23675 return 'center';
23676 }
23677 } else {
23678 return justification;
23679 }
23680};
23681
23682BRp$6.calculateLabelDimensions = function (ele, text) {
23683 var r = this;
23684 var cacheKey = hashString(text, ele._private.labelDimsKey);
23685 var cache = r.labelDimCache || (r.labelDimCache = []);
23686 var existingVal = cache[cacheKey];
23687
23688 if (existingVal != null) {
23689 return existingVal;
23690 }
23691
23692 var padding = 6; // add padding around text dims, as the measurement isn't that accurate
23693
23694 var fStyle = ele.pstyle('font-style').strValue;
23695 var size = ele.pstyle('font-size').pfValue;
23696 var family = ele.pstyle('font-family').strValue;
23697 var weight = ele.pstyle('font-weight').strValue;
23698 var canvas = this.labelCalcCanvas;
23699 var c2d = this.labelCalcCanvasContext;
23700
23701 if (!canvas) {
23702 canvas = this.labelCalcCanvas = document.createElement('canvas');
23703 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
23704 var ds = canvas.style;
23705 ds.position = 'absolute';
23706 ds.left = '-9999px';
23707 ds.top = '-9999px';
23708 ds.zIndex = '-1';
23709 ds.visibility = 'hidden';
23710 ds.pointerEvents = 'none';
23711 }
23712
23713 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
23714 var width = 0;
23715 var height = 0;
23716 var lines = text.split('\n');
23717
23718 for (var i = 0; i < lines.length; i++) {
23719 var line = lines[i];
23720 var metrics = c2d.measureText(line);
23721 var w = Math.ceil(metrics.width);
23722 var h = size;
23723 width = Math.max(w, width);
23724 height += h;
23725 }
23726
23727 width += padding;
23728 height += padding;
23729 return cache[cacheKey] = {
23730 width: width,
23731 height: height
23732 };
23733};
23734
23735BRp$6.calculateLabelAngle = function (ele, prefix) {
23736 var _p = ele._private;
23737 var rs = _p.rscratch;
23738 var isEdge = ele.isEdge();
23739 var prefixDash = prefix ? prefix + '-' : '';
23740 var rot = ele.pstyle(prefixDash + 'text-rotation');
23741 var rotStr = rot.strValue;
23742
23743 if (rotStr === 'none') {
23744 return 0;
23745 } else if (isEdge && rotStr === 'autorotate') {
23746 return rs.labelAutoAngle;
23747 } else if (rotStr === 'autorotate') {
23748 return 0;
23749 } else {
23750 return rot.pfValue;
23751 }
23752};
23753
23754BRp$6.calculateLabelAngles = function (ele) {
23755 var r = this;
23756 var isEdge = ele.isEdge();
23757 var _p = ele._private;
23758 var rs = _p.rscratch;
23759 rs.labelAngle = r.calculateLabelAngle(ele);
23760
23761 if (isEdge) {
23762 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
23763 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
23764 }
23765};
23766
23767var BRp$7 = {};
23768var TOO_SMALL_CUT_RECT = 28;
23769var warnedCutRect = false;
23770
23771BRp$7.getNodeShape = function (node) {
23772 var r = this;
23773 var shape = node.pstyle('shape').value;
23774
23775 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
23776 if (!warnedCutRect) {
23777 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
23778 warnedCutRect = true;
23779 }
23780
23781 return 'rectangle';
23782 }
23783
23784 if (node.isParent()) {
23785 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
23786 return shape;
23787 } else {
23788 return 'rectangle';
23789 }
23790 }
23791
23792 if (shape === 'polygon') {
23793 var points = node.pstyle('shape-polygon-points').value;
23794 return r.nodeShapes.makePolygon(points).name;
23795 }
23796
23797 return shape;
23798};
23799
23800var BRp$8 = {};
23801
23802BRp$8.registerCalculationListeners = function () {
23803 var cy = this.cy;
23804 var elesToUpdate = cy.collection();
23805 var r = this;
23806
23807 var enqueue = function enqueue(eles) {
23808 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
23809 elesToUpdate.merge(eles);
23810
23811 if (dirtyStyleCaches) {
23812 for (var i = 0; i < eles.length; i++) {
23813 var ele = eles[i];
23814 var _p = ele._private;
23815 var rstyle = _p.rstyle;
23816 rstyle.clean = false;
23817 rstyle.cleanConnected = false;
23818 }
23819 }
23820 };
23821
23822 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
23823 var ele = e.target;
23824 enqueue(ele);
23825 }).on('style.* background.*', function onDirtyStyle(e) {
23826 var ele = e.target;
23827 enqueue(ele, false);
23828 });
23829
23830 var updateEleCalcs = function updateEleCalcs(willDraw) {
23831 if (willDraw) {
23832 var fns = r.onUpdateEleCalcsFns;
23833
23834 for (var i = 0; i < elesToUpdate.length; i++) {
23835 var ele = elesToUpdate[i];
23836 var rstyle = ele._private.rstyle;
23837
23838 if (ele.isNode() && !rstyle.cleanConnected) {
23839 enqueue(ele.connectedEdges());
23840 rstyle.cleanConnected = true;
23841 }
23842 }
23843
23844 if (fns) {
23845 for (var _i = 0; _i < fns.length; _i++) {
23846 var fn = fns[_i];
23847 fn(willDraw, elesToUpdate);
23848 }
23849 }
23850
23851 r.recalculateRenderedStyle(elesToUpdate);
23852 elesToUpdate = cy.collection();
23853 }
23854 };
23855
23856 r.flushRenderedStyleQueue = function () {
23857 updateEleCalcs(true);
23858 };
23859
23860 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
23861};
23862
23863BRp$8.onUpdateEleCalcs = function (fn) {
23864 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
23865 fns.push(fn);
23866};
23867
23868BRp$8.recalculateRenderedStyle = function (eles, useCache) {
23869 var isCleanConnected = function isCleanConnected(ele) {
23870 return ele._private.rstyle.cleanConnected;
23871 };
23872
23873 var edges = [];
23874 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
23875
23876 if (this.destroyed) {
23877 return;
23878 } // use cache by default for perf
23879
23880
23881 if (useCache === undefined) {
23882 useCache = true;
23883 }
23884
23885 for (var i = 0; i < eles.length; i++) {
23886 var ele = eles[i];
23887 var _p = ele._private;
23888 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
23889 // (and a request for recalc may come in between frames)
23890
23891 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
23892 rstyle.clean = false;
23893 } // only update if dirty and in graph
23894
23895
23896 if (useCache && rstyle.clean || ele.removed()) {
23897 continue;
23898 } // only update if not display: none
23899
23900
23901 if (ele.pstyle('display').value === 'none') {
23902 continue;
23903 }
23904
23905 if (_p.group === 'nodes') {
23906 nodes.push(ele);
23907 } else {
23908 // edges
23909 edges.push(ele);
23910 }
23911
23912 rstyle.clean = true;
23913 } // update node data from projections
23914
23915
23916 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
23917 var _ele = nodes[_i2];
23918 var _p2 = _ele._private;
23919 var _rstyle = _p2.rstyle;
23920
23921 var pos = _ele.position();
23922
23923 this.recalculateNodeLabelProjection(_ele);
23924 _rstyle.nodeX = pos.x;
23925 _rstyle.nodeY = pos.y;
23926 _rstyle.nodeW = _ele.pstyle('width').pfValue;
23927 _rstyle.nodeH = _ele.pstyle('height').pfValue;
23928 }
23929
23930 this.recalculateEdgeProjections(edges); // update edge data from projections
23931
23932 for (var _i3 = 0; _i3 < edges.length; _i3++) {
23933 var _ele2 = edges[_i3];
23934 var _p3 = _ele2._private;
23935 var _rstyle2 = _p3.rstyle;
23936 var rs = _p3.rscratch; // update rstyle positions
23937
23938 _rstyle2.srcX = rs.arrowStartX;
23939 _rstyle2.srcY = rs.arrowStartY;
23940 _rstyle2.tgtX = rs.arrowEndX;
23941 _rstyle2.tgtY = rs.arrowEndY;
23942 _rstyle2.midX = rs.midX;
23943 _rstyle2.midY = rs.midY;
23944 _rstyle2.labelAngle = rs.labelAngle;
23945 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
23946 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
23947 }
23948};
23949
23950var BRp$9 = {};
23951
23952BRp$9.updateCachedGrabbedEles = function () {
23953 var eles = this.cachedZSortedEles;
23954
23955 if (!eles) {
23956 // just let this be recalculated on the next z sort tick
23957 return;
23958 }
23959
23960 eles.drag = [];
23961 eles.nondrag = [];
23962 var grabTargets = [];
23963
23964 for (var i = 0; i < eles.length; i++) {
23965 var ele = eles[i];
23966 var rs = ele._private.rscratch;
23967
23968 if (ele.grabbed() && !ele.isParent()) {
23969 grabTargets.push(ele);
23970 } else if (rs.inDragLayer) {
23971 eles.drag.push(ele);
23972 } else {
23973 eles.nondrag.push(ele);
23974 }
23975 } // put the grab target nodes last so it's on top of its neighbourhood
23976
23977
23978 for (var i = 0; i < grabTargets.length; i++) {
23979 var ele = grabTargets[i];
23980 eles.drag.push(ele);
23981 }
23982};
23983
23984BRp$9.invalidateCachedZSortedEles = function () {
23985 this.cachedZSortedEles = null;
23986};
23987
23988BRp$9.getCachedZSortedEles = function (forceRecalc) {
23989 if (forceRecalc || !this.cachedZSortedEles) {
23990 var eles = this.cy.mutableElements().toArray();
23991 eles.sort(zIndexSort);
23992 eles.interactive = eles.filter(function (ele) {
23993 return ele.interactive();
23994 });
23995 this.cachedZSortedEles = eles;
23996 this.updateCachedGrabbedEles();
23997 } else {
23998 eles = this.cachedZSortedEles;
23999 }
24000
24001 return eles;
24002};
24003
24004var BRp$a = {};
24005[BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
24006 extend(BRp$a, props);
24007});
24008
24009var BRp$b = {};
24010
24011BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
24012 var r = this;
24013 var imageCache = r.imageCache = r.imageCache || {};
24014 var cache = imageCache[url];
24015
24016 if (cache) {
24017 if (!cache.image.complete) {
24018 cache.image.addEventListener('load', onLoad);
24019 }
24020
24021 return cache.image;
24022 } else {
24023 cache = imageCache[url] = imageCache[url] || {};
24024 var image = cache.image = new Image(); // eslint-disable-line no-undef
24025
24026 image.addEventListener('load', onLoad);
24027 image.addEventListener('error', function () {
24028 image.error = true;
24029 }); // #1582 safari doesn't load data uris with crossOrigin properly
24030 // https://bugs.webkit.org/show_bug.cgi?id=123978
24031
24032 var dataUriPrefix = 'data:';
24033 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24034
24035 if (!isDataUri) {
24036 image.crossOrigin = crossOrigin; // prevent tainted canvas
24037 }
24038
24039 image.src = url;
24040 return image;
24041 }
24042};
24043
24044var BRp$c = {};
24045/* global document, window, ResizeObserver, MutationObserver */
24046
24047BRp$c.registerBinding = function (target, event, handler, useCapture) {
24048 // eslint-disable-line no-unused-vars
24049 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24050
24051 var b = this.binder(target);
24052 return b.on.apply(b, args);
24053};
24054
24055BRp$c.binder = function (tgt) {
24056 var r = this;
24057 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24058
24059 if (r.supportsPassiveEvents == null) {
24060 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24061 var supportsPassive = false;
24062
24063 try {
24064 var opts = Object.defineProperty({}, 'passive', {
24065 get: function get() {
24066 supportsPassive = true;
24067 return true;
24068 }
24069 });
24070 window.addEventListener('test', null, opts);
24071 } catch (err) {// not supported
24072 }
24073
24074 r.supportsPassiveEvents = supportsPassive;
24075 }
24076
24077 var on = function on(event, handler, useCapture) {
24078 var args = Array.prototype.slice.call(arguments);
24079
24080 if (tgtIsDom && r.supportsPassiveEvents) {
24081 // replace useCapture w/ opts obj
24082 args[2] = {
24083 capture: useCapture != null ? useCapture : false,
24084 passive: false,
24085 once: false
24086 };
24087 }
24088
24089 r.bindings.push({
24090 target: tgt,
24091 args: args
24092 });
24093 (tgt.addEventListener || tgt.on).apply(tgt, args);
24094 return this;
24095 };
24096
24097 return {
24098 on: on,
24099 addEventListener: on,
24100 addListener: on,
24101 bind: on
24102 };
24103};
24104
24105BRp$c.nodeIsDraggable = function (node) {
24106 return node && node.isNode() && !node.locked() && node.grabbable();
24107};
24108
24109BRp$c.nodeIsGrabbable = function (node) {
24110 return this.nodeIsDraggable(node) && node.interactive();
24111};
24112
24113BRp$c.load = function () {
24114 var r = this;
24115
24116 var isSelected = function isSelected(ele) {
24117 return ele.selected();
24118 };
24119
24120 var triggerEvents = function triggerEvents(target, names, e, position) {
24121 if (target == null) {
24122 target = r.cy;
24123 }
24124
24125 for (var i = 0; i < names.length; i++) {
24126 var name = names[i];
24127 target.emit({
24128 originalEvent: e,
24129 type: name,
24130 position: position
24131 });
24132 }
24133 };
24134
24135 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24136 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24137 };
24138
24139 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24140 var allowPassthrough = true;
24141
24142 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24143 // a grabbable compound node below the ele => no passthrough panning
24144 for (var i = 0; downs && i < downs.length; i++) {
24145 var down = downs[i];
24146
24147 if (down.isNode() && down.isParent()) {
24148 allowPassthrough = false;
24149 break;
24150 }
24151 }
24152 } else {
24153 allowPassthrough = true;
24154 }
24155
24156 return allowPassthrough;
24157 };
24158
24159 var setGrabbed = function setGrabbed(ele) {
24160 ele[0]._private.grabbed = true;
24161 };
24162
24163 var setFreed = function setFreed(ele) {
24164 ele[0]._private.grabbed = false;
24165 };
24166
24167 var setInDragLayer = function setInDragLayer(ele) {
24168 ele[0]._private.rscratch.inDragLayer = true;
24169 };
24170
24171 var setOutDragLayer = function setOutDragLayer(ele) {
24172 ele[0]._private.rscratch.inDragLayer = false;
24173 };
24174
24175 var setGrabTarget = function setGrabTarget(ele) {
24176 ele[0]._private.rscratch.isGrabTarget = true;
24177 };
24178
24179 var removeGrabTarget = function removeGrabTarget(ele) {
24180 ele[0]._private.rscratch.isGrabTarget = false;
24181 };
24182
24183 var addToDragList = function addToDragList(ele, opts) {
24184 var list = opts.addToList;
24185 var listHasEle = list.has(ele);
24186
24187 if (!listHasEle) {
24188 list.merge(ele);
24189 setGrabbed(ele);
24190 }
24191 }; // helper function to determine which child nodes and inner edges
24192 // of a compound node to be dragged as well as the grabbed and selected nodes
24193
24194
24195 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24196 if (!node.cy().hasCompoundNodes()) {
24197 return;
24198 }
24199
24200 if (opts.inDragLayer == null && opts.addToList == null) {
24201 return;
24202 } // nothing to do
24203
24204
24205 var innerNodes = node.descendants();
24206
24207 if (opts.inDragLayer) {
24208 innerNodes.forEach(setInDragLayer);
24209 innerNodes.connectedEdges().forEach(setInDragLayer);
24210 }
24211
24212 if (opts.addToList) {
24213 opts.addToList.unmerge(innerNodes);
24214 }
24215 }; // adds the given nodes and its neighbourhood to the drag layer
24216
24217
24218 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24219 opts = opts || {};
24220 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24221
24222 if (opts.inDragLayer) {
24223 nodes.forEach(setInDragLayer);
24224 nodes.neighborhood().stdFilter(function (ele) {
24225 return !hasCompoundNodes || ele.isEdge();
24226 }).forEach(setInDragLayer);
24227 }
24228
24229 if (opts.addToList) {
24230 nodes.forEach(function (ele) {
24231 addToDragList(ele, opts);
24232 });
24233 }
24234
24235 addDescendantsToDrag(nodes, opts); // always add to drag
24236 // also add nodes and edges related to the topmost ancestor
24237
24238 updateAncestorsInDragLayer(nodes, {
24239 inDragLayer: opts.inDragLayer
24240 });
24241 r.updateCachedGrabbedEles();
24242 };
24243
24244 var addNodeToDrag = addNodesToDrag;
24245
24246 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24247 if (!grabbedEles) {
24248 return;
24249 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24250
24251
24252 r.getCachedZSortedEles().forEach(function (ele) {
24253 setFreed(ele);
24254 setOutDragLayer(ele);
24255 removeGrabTarget(ele);
24256 });
24257 r.updateCachedGrabbedEles();
24258 }; // helper function to determine which ancestor nodes and edges should go
24259 // to the drag layer (or should be removed from drag layer).
24260
24261
24262 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24263 if (opts.inDragLayer == null && opts.addToList == null) {
24264 return;
24265 } // nothing to do
24266
24267
24268 if (!node.cy().hasCompoundNodes()) {
24269 return;
24270 } // find top-level parent
24271
24272
24273 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
24274
24275 if (parent.same(node)) {
24276 return;
24277 }
24278
24279 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24280 var edges = nodes.connectedEdges();
24281
24282 if (opts.inDragLayer) {
24283 edges.forEach(setInDragLayer);
24284 nodes.forEach(setInDragLayer);
24285 }
24286
24287 if (opts.addToList) {
24288 nodes.forEach(function (ele) {
24289 addToDragList(ele, opts);
24290 });
24291 }
24292 };
24293
24294 var blurActiveDomElement = function blurActiveDomElement() {
24295 if (document.activeElement != null && document.activeElement.blur != null) {
24296 document.activeElement.blur();
24297 }
24298 };
24299
24300 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24301 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
24302
24303 if (haveMutationsApi) {
24304 r.removeObserver = new MutationObserver(function (mutns) {
24305 // eslint-disable-line no-undef
24306 for (var i = 0; i < mutns.length; i++) {
24307 var mutn = mutns[i];
24308 var rNodes = mutn.removedNodes;
24309
24310 if (rNodes) {
24311 for (var j = 0; j < rNodes.length; j++) {
24312 var rNode = rNodes[j];
24313
24314 if (rNode === r.container) {
24315 r.destroy();
24316 break;
24317 }
24318 }
24319 }
24320 }
24321 });
24322
24323 if (r.container.parentNode) {
24324 r.removeObserver.observe(r.container.parentNode, {
24325 childList: true
24326 });
24327 }
24328 } else {
24329 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24330 // eslint-disable-line no-unused-vars
24331 r.destroy();
24332 });
24333 }
24334
24335 var onResize = util(function () {
24336 r.cy.resize();
24337 }, 100);
24338
24339 if (haveMutationsApi) {
24340 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24341
24342 r.styleObserver.observe(r.container, {
24343 attributes: true
24344 });
24345 } // auto resize
24346
24347
24348 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
24349
24350 if (haveResizeObserverApi) {
24351 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24352
24353 r.resizeObserver.observe(r.container);
24354 }
24355
24356 var forEachUp = function forEachUp(domEle, fn) {
24357 while (domEle != null) {
24358 fn(domEle);
24359 domEle = domEle.parentNode;
24360 }
24361 };
24362
24363 var invalidateCoords = function invalidateCoords() {
24364 r.invalidateContainerClientCoordsCache();
24365 };
24366
24367 forEachUp(r.container, function (domEle) {
24368 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24369 r.registerBinding(domEle, 'animationend', invalidateCoords);
24370 r.registerBinding(domEle, 'scroll', invalidateCoords);
24371 }); // stop right click menu from appearing on cy
24372
24373 r.registerBinding(r.container, 'contextmenu', function (e) {
24374 e.preventDefault();
24375 });
24376
24377 var inBoxSelection = function inBoxSelection() {
24378 return r.selection[4] !== 0;
24379 };
24380
24381 var eventInContainer = function eventInContainer(e) {
24382 // save cycles if mouse events aren't to be captured
24383 var containerPageCoords = r.findContainerClientCoords();
24384 var x = containerPageCoords[0];
24385 var y = containerPageCoords[1];
24386 var width = containerPageCoords[2];
24387 var height = containerPageCoords[3];
24388 var positions = e.touches ? e.touches : [e];
24389 var atLeastOnePosInside = false;
24390
24391 for (var i = 0; i < positions.length; i++) {
24392 var p = positions[i];
24393
24394 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24395 atLeastOnePosInside = true;
24396 break;
24397 }
24398 }
24399
24400 if (!atLeastOnePosInside) {
24401 return false;
24402 }
24403
24404 var container = r.container;
24405 var target = e.target;
24406 var tParent = target.parentNode;
24407 var containerIsTarget = false;
24408
24409 while (tParent) {
24410 if (tParent === container) {
24411 containerIsTarget = true;
24412 break;
24413 }
24414
24415 tParent = tParent.parentNode;
24416 }
24417
24418 if (!containerIsTarget) {
24419 return false;
24420 } // if target is outisde cy container, then this event is not for us
24421
24422
24423 return true;
24424 }; // Primary key
24425
24426
24427 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24428 if (!eventInContainer(e)) {
24429 return;
24430 }
24431
24432 e.preventDefault();
24433 blurActiveDomElement();
24434 r.hoverData.capture = true;
24435 r.hoverData.which = e.which;
24436 var cy = r.cy;
24437 var gpos = [e.clientX, e.clientY];
24438 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24439 var select = r.selection;
24440 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24441 var near = nears[0];
24442 var draggedElements = r.dragData.possibleDragElements;
24443 r.hoverData.mdownPos = pos;
24444 r.hoverData.mdownGPos = gpos;
24445
24446 var checkForTaphold = function checkForTaphold() {
24447 r.hoverData.tapholdCancelled = false;
24448 clearTimeout(r.hoverData.tapholdTimeout);
24449 r.hoverData.tapholdTimeout = setTimeout(function () {
24450 if (r.hoverData.tapholdCancelled) {
24451 return;
24452 } else {
24453 var ele = r.hoverData.down;
24454
24455 if (ele) {
24456 ele.emit({
24457 originalEvent: e,
24458 type: 'taphold',
24459 position: {
24460 x: pos[0],
24461 y: pos[1]
24462 }
24463 });
24464 } else {
24465 cy.emit({
24466 originalEvent: e,
24467 type: 'taphold',
24468 position: {
24469 x: pos[0],
24470 y: pos[1]
24471 }
24472 });
24473 }
24474 }
24475 }, r.tapholdDuration);
24476 }; // Right click button
24477
24478
24479 if (e.which == 3) {
24480 r.hoverData.cxtStarted = true;
24481 var cxtEvt = {
24482 originalEvent: e,
24483 type: 'cxttapstart',
24484 position: {
24485 x: pos[0],
24486 y: pos[1]
24487 }
24488 };
24489
24490 if (near) {
24491 near.activate();
24492 near.emit(cxtEvt);
24493 r.hoverData.down = near;
24494 } else {
24495 cy.emit(cxtEvt);
24496 }
24497
24498 r.hoverData.downTime = new Date().getTime();
24499 r.hoverData.cxtDragged = false; // Primary button
24500 } else if (e.which == 1) {
24501 if (near) {
24502 near.activate();
24503 } // Element dragging
24504
24505
24506 {
24507 // If something is under the cursor and it is draggable, prepare to grab it
24508 if (near != null) {
24509 if (r.nodeIsGrabbable(near)) {
24510 var makeEvent = function makeEvent(type) {
24511 return {
24512 originalEvent: e,
24513 type: type,
24514 position: {
24515 x: pos[0],
24516 y: pos[1]
24517 }
24518 };
24519 };
24520
24521 var triggerGrab = function triggerGrab(ele) {
24522 ele.emit(makeEvent('grab'));
24523 };
24524
24525 setGrabTarget(near);
24526
24527 if (!near.selected()) {
24528 draggedElements = r.dragData.possibleDragElements = cy.collection();
24529 addNodeToDrag(near, {
24530 addToList: draggedElements
24531 });
24532 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24533 } else {
24534 draggedElements = r.dragData.possibleDragElements = cy.collection();
24535 var selectedNodes = cy.$(function (ele) {
24536 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24537 });
24538 addNodesToDrag(selectedNodes, {
24539 addToList: draggedElements
24540 });
24541 near.emit(makeEvent('grabon'));
24542 selectedNodes.forEach(triggerGrab);
24543 }
24544
24545 r.redrawHint('eles', true);
24546 r.redrawHint('drag', true);
24547 }
24548 }
24549
24550 r.hoverData.down = near;
24551 r.hoverData.downs = nears;
24552 r.hoverData.downTime = new Date().getTime();
24553 }
24554 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24555 x: pos[0],
24556 y: pos[1]
24557 });
24558
24559 if (near == null) {
24560 select[4] = 1;
24561 r.data.bgActivePosistion = {
24562 x: pos[0],
24563 y: pos[1]
24564 };
24565 r.redrawHint('select', true);
24566 r.redraw();
24567 } else if (near.pannable()) {
24568 select[4] = 1; // for future pan
24569 }
24570
24571 checkForTaphold();
24572 } // Initialize selection box coordinates
24573
24574
24575 select[0] = select[2] = pos[0];
24576 select[1] = select[3] = pos[1];
24577 }, false);
24578 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
24579 // eslint-disable-line no-undef
24580 var capture = r.hoverData.capture;
24581
24582 if (!capture && !eventInContainer(e)) {
24583 return;
24584 }
24585
24586 var preventDefault = false;
24587 var cy = r.cy;
24588 var zoom = cy.zoom();
24589 var gpos = [e.clientX, e.clientY];
24590 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24591 var mdownPos = r.hoverData.mdownPos;
24592 var mdownGPos = r.hoverData.mdownGPos;
24593 var select = r.selection;
24594 var near = null;
24595
24596 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24597 near = r.findNearestElement(pos[0], pos[1], true, false);
24598 }
24599
24600 var last = r.hoverData.last;
24601 var down = r.hoverData.down;
24602 var disp = [pos[0] - select[2], pos[1] - select[3]];
24603 var draggedElements = r.dragData.possibleDragElements;
24604 var isOverThresholdDrag;
24605
24606 if (mdownGPos) {
24607 var dx = gpos[0] - mdownGPos[0];
24608 var dx2 = dx * dx;
24609 var dy = gpos[1] - mdownGPos[1];
24610 var dy2 = dy * dy;
24611 var dist2 = dx2 + dy2;
24612 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24613 }
24614
24615 var multSelKeyDown = isMultSelKeyDown(e);
24616
24617 if (isOverThresholdDrag) {
24618 r.hoverData.tapholdCancelled = true;
24619 }
24620
24621 var updateDragDelta = function updateDragDelta() {
24622 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24623
24624 if (dragDelta.length === 0) {
24625 dragDelta.push(disp[0]);
24626 dragDelta.push(disp[1]);
24627 } else {
24628 dragDelta[0] += disp[0];
24629 dragDelta[1] += disp[1];
24630 }
24631 };
24632
24633 preventDefault = true;
24634 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24635 x: pos[0],
24636 y: pos[1]
24637 });
24638
24639 var goIntoBoxMode = function goIntoBoxMode() {
24640 r.data.bgActivePosistion = undefined;
24641
24642 if (!r.hoverData.selecting) {
24643 cy.emit({
24644 originalEvent: e,
24645 type: 'boxstart',
24646 position: {
24647 x: pos[0],
24648 y: pos[1]
24649 }
24650 });
24651 }
24652
24653 select[4] = 1;
24654 r.hoverData.selecting = true;
24655 r.redrawHint('select', true);
24656 r.redraw();
24657 }; // trigger context drag if rmouse down
24658
24659
24660 if (r.hoverData.which === 3) {
24661 // but only if over threshold
24662 if (isOverThresholdDrag) {
24663 var cxtEvt = {
24664 originalEvent: e,
24665 type: 'cxtdrag',
24666 position: {
24667 x: pos[0],
24668 y: pos[1]
24669 }
24670 };
24671
24672 if (down) {
24673 down.emit(cxtEvt);
24674 } else {
24675 cy.emit(cxtEvt);
24676 }
24677
24678 r.hoverData.cxtDragged = true;
24679
24680 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24681 if (r.hoverData.cxtOver) {
24682 r.hoverData.cxtOver.emit({
24683 originalEvent: e,
24684 type: 'cxtdragout',
24685 position: {
24686 x: pos[0],
24687 y: pos[1]
24688 }
24689 });
24690 }
24691
24692 r.hoverData.cxtOver = near;
24693
24694 if (near) {
24695 near.emit({
24696 originalEvent: e,
24697 type: 'cxtdragover',
24698 position: {
24699 x: pos[0],
24700 y: pos[1]
24701 }
24702 });
24703 }
24704 }
24705 } // Check if we are drag panning the entire graph
24706
24707 } else if (r.hoverData.dragging) {
24708 preventDefault = true;
24709
24710 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24711 var deltaP;
24712
24713 if (r.hoverData.justStartedPan) {
24714 var mdPos = r.hoverData.mdownPos;
24715 deltaP = {
24716 x: (pos[0] - mdPos[0]) * zoom,
24717 y: (pos[1] - mdPos[1]) * zoom
24718 };
24719 r.hoverData.justStartedPan = false;
24720 } else {
24721 deltaP = {
24722 x: disp[0] * zoom,
24723 y: disp[1] * zoom
24724 };
24725 }
24726
24727 cy.panBy(deltaP);
24728 r.hoverData.dragged = true;
24729 } // Needs reproject due to pan changing viewport
24730
24731
24732 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24733 } else if (select[4] == 1 && (down == null || down.pannable())) {
24734 if (isOverThresholdDrag) {
24735 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24736 goIntoBoxMode();
24737 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24738 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24739
24740 if (allowPassthrough) {
24741 r.hoverData.dragging = true;
24742 r.hoverData.justStartedPan = true;
24743 select[4] = 0;
24744 r.data.bgActivePosistion = array2point(mdownPos);
24745 r.redrawHint('select', true);
24746 r.redraw();
24747 }
24748 }
24749
24750 if (down && down.pannable() && down.active()) {
24751 down.unactivate();
24752 }
24753 }
24754 } else {
24755 if (down && down.pannable() && down.active()) {
24756 down.unactivate();
24757 }
24758
24759 if ((!down || !down.grabbed()) && near != last) {
24760 if (last) {
24761 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24762 x: pos[0],
24763 y: pos[1]
24764 });
24765 }
24766
24767 if (near) {
24768 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24769 x: pos[0],
24770 y: pos[1]
24771 });
24772 }
24773
24774 r.hoverData.last = near;
24775 }
24776
24777 if (down) {
24778 if (isOverThresholdDrag) {
24779 // then we can take action
24780 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24781 // then selection overrides
24782 if (down && down.grabbed()) {
24783 freeDraggedElements(draggedElements);
24784 down.emit('freeon');
24785 draggedElements.emit('free');
24786
24787 if (r.dragData.didDrag) {
24788 down.emit('dragfreeon');
24789 draggedElements.emit('dragfree');
24790 }
24791 }
24792
24793 goIntoBoxMode();
24794 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24795 // drag node
24796 var justStartedDrag = !r.dragData.didDrag;
24797
24798 if (justStartedDrag) {
24799 r.redrawHint('eles', true);
24800 }
24801
24802 r.dragData.didDrag = true; // indicate that we actually did drag the node
24803
24804 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
24805
24806 if (!r.hoverData.draggingEles) {
24807 addNodesToDrag(draggedElements, {
24808 inDragLayer: true
24809 });
24810 }
24811
24812 var totalShift = {
24813 x: 0,
24814 y: 0
24815 };
24816
24817 if (number(disp[0]) && number(disp[1])) {
24818 totalShift.x += disp[0];
24819 totalShift.y += disp[1];
24820
24821 if (justStartedDrag) {
24822 var dragDelta = r.hoverData.dragDelta;
24823
24824 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
24825 totalShift.x += dragDelta[0];
24826 totalShift.y += dragDelta[1];
24827 }
24828 }
24829 }
24830
24831 for (var i = 0; i < draggedElements.length; i++) {
24832 var dEle = draggedElements[i];
24833
24834 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
24835 toTrigger.push(dEle);
24836 }
24837 }
24838
24839 r.hoverData.draggingEles = true;
24840 toTrigger.silentShift(totalShift).emit('position drag');
24841 r.redrawHint('drag', true);
24842 r.redraw();
24843 }
24844 } else {
24845 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
24846 updateDragDelta();
24847 }
24848 } // prevent the dragging from triggering text selection on the page
24849
24850
24851 preventDefault = true;
24852 }
24853
24854 select[2] = pos[0];
24855 select[3] = pos[1];
24856
24857 if (preventDefault) {
24858 if (e.stopPropagation) e.stopPropagation();
24859 if (e.preventDefault) e.preventDefault();
24860 return false;
24861 }
24862 }, false);
24863 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
24864 // eslint-disable-line no-undef
24865 var capture = r.hoverData.capture;
24866
24867 if (!capture) {
24868 return;
24869 }
24870
24871 r.hoverData.capture = false;
24872 var cy = r.cy;
24873 var pos = r.projectIntoViewport(e.clientX, e.clientY);
24874 var select = r.selection;
24875 var near = r.findNearestElement(pos[0], pos[1], true, false);
24876 var draggedElements = r.dragData.possibleDragElements;
24877 var down = r.hoverData.down;
24878 var multSelKeyDown = isMultSelKeyDown(e);
24879
24880 if (r.data.bgActivePosistion) {
24881 r.redrawHint('select', true);
24882 r.redraw();
24883 }
24884
24885 r.hoverData.tapholdCancelled = true;
24886 r.data.bgActivePosistion = undefined; // not active bg now
24887
24888 if (down) {
24889 down.unactivate();
24890 }
24891
24892 if (r.hoverData.which === 3) {
24893 var cxtEvt = {
24894 originalEvent: e,
24895 type: 'cxttapend',
24896 position: {
24897 x: pos[0],
24898 y: pos[1]
24899 }
24900 };
24901
24902 if (down) {
24903 down.emit(cxtEvt);
24904 } else {
24905 cy.emit(cxtEvt);
24906 }
24907
24908 if (!r.hoverData.cxtDragged) {
24909 var cxtTap = {
24910 originalEvent: e,
24911 type: 'cxttap',
24912 position: {
24913 x: pos[0],
24914 y: pos[1]
24915 }
24916 };
24917
24918 if (down) {
24919 down.emit(cxtTap);
24920 } else {
24921 cy.emit(cxtTap);
24922 }
24923 }
24924
24925 r.hoverData.cxtDragged = false;
24926 r.hoverData.which = null;
24927 } else if (r.hoverData.which === 1) {
24928 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
24929 x: pos[0],
24930 y: pos[1]
24931 });
24932
24933 if (!r.dragData.didDrag // didn't move a node around
24934 && !r.hoverData.dragged // didn't pan
24935 && !r.hoverData.selecting // not box selection
24936 && !r.hoverData.isOverThresholdDrag // didn't move too much
24937 ) {
24938 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
24939 x: pos[0],
24940 y: pos[1]
24941 });
24942 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
24943
24944
24945 if (down == null && // not mousedown on node
24946 !r.dragData.didDrag // didn't move the node around
24947 && !r.hoverData.selecting // not box selection
24948 && !r.hoverData.dragged // didn't pan
24949 && !isMultSelKeyDown(e)) {
24950 cy.$(isSelected).unselect(['tapunselect']);
24951
24952 if (draggedElements.length > 0) {
24953 r.redrawHint('eles', true);
24954 }
24955
24956 r.dragData.possibleDragElements = draggedElements = cy.collection();
24957 } // Single selection
24958
24959
24960 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
24961 if (near != null && near._private.selectable) {
24962 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
24963 if (near.selected()) {
24964 near.unselect(['tapunselect']);
24965 } else {
24966 near.select(['tapselect']);
24967 }
24968 } else {
24969 if (!multSelKeyDown) {
24970 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
24971 near.select(['tapselect']);
24972 }
24973 }
24974
24975 r.redrawHint('eles', true);
24976 }
24977 }
24978
24979 if (r.hoverData.selecting) {
24980 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
24981 r.redrawHint('select', true);
24982
24983 if (box.length > 0) {
24984 r.redrawHint('eles', true);
24985 }
24986
24987 cy.emit({
24988 type: 'boxend',
24989 originalEvent: e,
24990 position: {
24991 x: pos[0],
24992 y: pos[1]
24993 }
24994 });
24995
24996 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
24997 return ele.selectable() && !ele.selected();
24998 };
24999
25000 if (cy.selectionType() === 'additive') {
25001 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25002 } else {
25003 if (!multSelKeyDown) {
25004 cy.$(isSelected).unmerge(box).unselect();
25005 }
25006
25007 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25008 } // always need redraw in case eles unselectable
25009
25010
25011 r.redraw();
25012 } // Cancel drag pan
25013
25014
25015 if (r.hoverData.dragging) {
25016 r.hoverData.dragging = false;
25017 r.redrawHint('select', true);
25018 r.redrawHint('eles', true);
25019 r.redraw();
25020 }
25021
25022 if (!select[4]) {
25023 r.redrawHint('drag', true);
25024 r.redrawHint('eles', true);
25025 var downWasGrabbed = down && down.grabbed();
25026 freeDraggedElements(draggedElements);
25027
25028 if (downWasGrabbed) {
25029 down.emit('freeon');
25030 draggedElements.emit('free');
25031
25032 if (r.dragData.didDrag) {
25033 down.emit('dragfreeon');
25034 draggedElements.emit('dragfree');
25035 }
25036 }
25037 }
25038 } // else not right mouse
25039
25040
25041 select[4] = 0;
25042 r.hoverData.down = null;
25043 r.hoverData.cxtStarted = false;
25044 r.hoverData.draggingEles = false;
25045 r.hoverData.selecting = false;
25046 r.hoverData.isOverThresholdDrag = false;
25047 r.dragData.didDrag = false;
25048 r.hoverData.dragged = false;
25049 r.hoverData.dragDelta = [];
25050 r.hoverData.mdownPos = null;
25051 r.hoverData.mdownGPos = null;
25052 }, false);
25053
25054 var wheelHandler = function wheelHandler(e) {
25055 if (r.scrollingPage) {
25056 return;
25057 } // while scrolling, ignore wheel-to-zoom
25058
25059
25060 var cy = r.cy;
25061 var zoom = cy.zoom();
25062 var pan = cy.pan();
25063 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25064 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25065
25066 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25067 // if pan dragging or cxt dragging, wheel movements make no zoom
25068 e.preventDefault();
25069 return;
25070 }
25071
25072 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25073 e.preventDefault();
25074 r.data.wheelZooming = true;
25075 clearTimeout(r.data.wheelTimeout);
25076 r.data.wheelTimeout = setTimeout(function () {
25077 r.data.wheelZooming = false;
25078 r.redrawHint('eles', true);
25079 r.redraw();
25080 }, 150);
25081 var diff;
25082
25083 if (e.deltaY != null) {
25084 diff = e.deltaY / -250;
25085 } else if (e.wheelDeltaY != null) {
25086 diff = e.wheelDeltaY / 1000;
25087 } else {
25088 diff = e.wheelDelta / 1000;
25089 }
25090
25091 diff = diff * r.wheelSensitivity;
25092 var needsWheelFix = e.deltaMode === 1;
25093
25094 if (needsWheelFix) {
25095 // fixes slow wheel events on ff/linux and ff/windows
25096 diff *= 33;
25097 }
25098
25099 var newZoom = cy.zoom() * Math.pow(10, diff);
25100
25101 if (e.type === 'gesturechange') {
25102 newZoom = r.gestureStartZoom * e.scale;
25103 }
25104
25105 cy.zoom({
25106 level: newZoom,
25107 renderedPosition: {
25108 x: rpos[0],
25109 y: rpos[1]
25110 }
25111 });
25112 }
25113 }; // Functions to help with whether mouse wheel should trigger zooming
25114 // --
25115
25116
25117 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25118 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25119 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25120 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25121
25122 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25123 // eslint-disable-line no-unused-vars
25124 r.scrollingPage = true;
25125 clearTimeout(r.scrollingPageTimeout);
25126 r.scrollingPageTimeout = setTimeout(function () {
25127 r.scrollingPage = false;
25128 }, 250);
25129 }, true); // desktop safari pinch to zoom start
25130
25131 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25132 r.gestureStartZoom = r.cy.zoom();
25133
25134 if (!r.hasTouchStarted) {
25135 // don't affect touch devices like iphone
25136 e.preventDefault();
25137 }
25138 }, true);
25139 r.registerBinding(r.container, 'gesturechange', function (e) {
25140 if (!r.hasTouchStarted) {
25141 // don't affect touch devices like iphone
25142 wheelHandler(e);
25143 }
25144 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25145 // Handle mouseout on Cytoscape container
25146
25147 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25148 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25149 r.cy.emit({
25150 originalEvent: e,
25151 type: 'mouseout',
25152 position: {
25153 x: pos[0],
25154 y: pos[1]
25155 }
25156 });
25157 }, false);
25158 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25159 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25160 r.cy.emit({
25161 originalEvent: e,
25162 type: 'mouseover',
25163 position: {
25164 x: pos[0],
25165 y: pos[1]
25166 }
25167 });
25168 }, false);
25169 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25170
25171 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25172
25173 var center1, modelCenter1; // center point on start pinch to zoom
25174
25175 var offsetLeft, offsetTop;
25176 var containerWidth, containerHeight;
25177 var twoFingersStartInside;
25178
25179 var distance = function distance(x1, y1, x2, y2) {
25180 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25181 };
25182
25183 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25184 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25185 };
25186
25187 var touchstartHandler;
25188 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25189 r.hasTouchStarted = true;
25190
25191 if (!eventInContainer(e)) {
25192 return;
25193 }
25194
25195 blurActiveDomElement();
25196 r.touchData.capture = true;
25197 r.data.bgActivePosistion = undefined;
25198 var cy = r.cy;
25199 var now = r.touchData.now;
25200 var earlier = r.touchData.earlier;
25201
25202 if (e.touches[0]) {
25203 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25204 now[0] = pos[0];
25205 now[1] = pos[1];
25206 }
25207
25208 if (e.touches[1]) {
25209 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25210 now[2] = pos[0];
25211 now[3] = pos[1];
25212 }
25213
25214 if (e.touches[2]) {
25215 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25216 now[4] = pos[0];
25217 now[5] = pos[1];
25218 } // record starting points for pinch-to-zoom
25219
25220
25221 if (e.touches[1]) {
25222 r.touchData.singleTouchMoved = true;
25223 freeDraggedElements(r.dragData.touchDragEles);
25224 var offsets = r.findContainerClientCoords();
25225 offsetLeft = offsets[0];
25226 offsetTop = offsets[1];
25227 containerWidth = offsets[2];
25228 containerHeight = offsets[3];
25229 f1x1 = e.touches[0].clientX - offsetLeft;
25230 f1y1 = e.touches[0].clientY - offsetTop;
25231 f2x1 = e.touches[1].clientX - offsetLeft;
25232 f2y1 = e.touches[1].clientY - offsetTop;
25233 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25234 var pan = cy.pan();
25235 var zoom = cy.zoom();
25236 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25237 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25238 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25239 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25240
25241 var cxtDistThreshold = 200;
25242 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25243
25244 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25245 var near1 = r.findNearestElement(now[0], now[1], true, true);
25246 var near2 = r.findNearestElement(now[2], now[3], true, true);
25247
25248 if (near1 && near1.isNode()) {
25249 near1.activate().emit({
25250 originalEvent: e,
25251 type: 'cxttapstart',
25252 position: {
25253 x: now[0],
25254 y: now[1]
25255 }
25256 });
25257 r.touchData.start = near1;
25258 } else if (near2 && near2.isNode()) {
25259 near2.activate().emit({
25260 originalEvent: e,
25261 type: 'cxttapstart',
25262 position: {
25263 x: now[0],
25264 y: now[1]
25265 }
25266 });
25267 r.touchData.start = near2;
25268 } else {
25269 cy.emit({
25270 originalEvent: e,
25271 type: 'cxttapstart',
25272 position: {
25273 x: now[0],
25274 y: now[1]
25275 }
25276 });
25277 }
25278
25279 if (r.touchData.start) {
25280 r.touchData.start._private.grabbed = false;
25281 }
25282
25283 r.touchData.cxt = true;
25284 r.touchData.cxtDragged = false;
25285 r.data.bgActivePosistion = undefined;
25286 r.redraw();
25287 return;
25288 }
25289 }
25290
25291 if (e.touches[2]) {
25292 // ignore
25293 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25294 if (cy.boxSelectionEnabled()) {
25295 e.preventDefault();
25296 }
25297 } else if (e.touches[1]) ; else if (e.touches[0]) {
25298 var nears = r.findNearestElements(now[0], now[1], true, true);
25299 var near = nears[0];
25300
25301 if (near != null) {
25302 near.activate();
25303 r.touchData.start = near;
25304 r.touchData.starts = nears;
25305
25306 if (r.nodeIsGrabbable(near)) {
25307 var draggedEles = r.dragData.touchDragEles = cy.collection();
25308 var selectedNodes = null;
25309 r.redrawHint('eles', true);
25310 r.redrawHint('drag', true);
25311
25312 if (near.selected()) {
25313 // reset drag elements, since near will be added again
25314 selectedNodes = cy.$(function (ele) {
25315 return ele.selected() && r.nodeIsGrabbable(ele);
25316 });
25317 addNodesToDrag(selectedNodes, {
25318 addToList: draggedEles
25319 });
25320 } else {
25321 addNodeToDrag(near, {
25322 addToList: draggedEles
25323 });
25324 }
25325
25326 setGrabTarget(near);
25327
25328 var makeEvent = function makeEvent(type) {
25329 return {
25330 originalEvent: e,
25331 type: type,
25332 position: {
25333 x: now[0],
25334 y: now[1]
25335 }
25336 };
25337 };
25338
25339 near.emit(makeEvent('grabon'));
25340
25341 if (selectedNodes) {
25342 selectedNodes.forEach(function (n) {
25343 n.emit(makeEvent('grab'));
25344 });
25345 } else {
25346 near.emit(makeEvent('grab'));
25347 }
25348 }
25349 }
25350
25351 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25352 x: now[0],
25353 y: now[1]
25354 });
25355
25356 if (near == null) {
25357 r.data.bgActivePosistion = {
25358 x: pos[0],
25359 y: pos[1]
25360 };
25361 r.redrawHint('select', true);
25362 r.redraw();
25363 } // Tap, taphold
25364 // -----
25365
25366
25367 r.touchData.singleTouchMoved = false;
25368 r.touchData.singleTouchStartTime = +new Date();
25369 clearTimeout(r.touchData.tapholdTimeout);
25370 r.touchData.tapholdTimeout = setTimeout(function () {
25371 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25372 && !r.touchData.selecting // box selection shouldn't allow taphold through
25373 ) {
25374 triggerEvents(r.touchData.start, ['taphold'], e, {
25375 x: now[0],
25376 y: now[1]
25377 });
25378 }
25379 }, r.tapholdDuration);
25380 }
25381
25382 if (e.touches.length >= 1) {
25383 var sPos = r.touchData.startPosition = [];
25384
25385 for (var i = 0; i < now.length; i++) {
25386 sPos[i] = earlier[i] = now[i];
25387 }
25388
25389 var touch0 = e.touches[0];
25390 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25391 }
25392 }, false);
25393 var touchmoveHandler;
25394 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25395 // eslint-disable-line no-undef
25396 var capture = r.touchData.capture;
25397
25398 if (!capture && !eventInContainer(e)) {
25399 return;
25400 }
25401
25402 var select = r.selection;
25403 var cy = r.cy;
25404 var now = r.touchData.now;
25405 var earlier = r.touchData.earlier;
25406 var zoom = cy.zoom();
25407
25408 if (e.touches[0]) {
25409 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25410 now[0] = pos[0];
25411 now[1] = pos[1];
25412 }
25413
25414 if (e.touches[1]) {
25415 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25416 now[2] = pos[0];
25417 now[3] = pos[1];
25418 }
25419
25420 if (e.touches[2]) {
25421 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25422 now[4] = pos[0];
25423 now[5] = pos[1];
25424 }
25425
25426 var startGPos = r.touchData.startGPosition;
25427 var isOverThresholdDrag;
25428
25429 if (capture && e.touches[0] && startGPos) {
25430 var disp = [];
25431
25432 for (var j = 0; j < now.length; j++) {
25433 disp[j] = now[j] - earlier[j];
25434 }
25435
25436 var dx = e.touches[0].clientX - startGPos[0];
25437 var dx2 = dx * dx;
25438 var dy = e.touches[0].clientY - startGPos[1];
25439 var dy2 = dy * dy;
25440 var dist2 = dx2 + dy2;
25441 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25442 } // context swipe cancelling
25443
25444
25445 if (capture && r.touchData.cxt) {
25446 e.preventDefault();
25447 var f1x2 = e.touches[0].clientX - offsetLeft,
25448 f1y2 = e.touches[0].clientY - offsetTop;
25449 var f2x2 = e.touches[1].clientX - offsetLeft,
25450 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25451
25452 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25453 var factorSq = distance2Sq / distance1Sq;
25454 var distThreshold = 150;
25455 var distThresholdSq = distThreshold * distThreshold;
25456 var factorThreshold = 1.5;
25457 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25458
25459 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25460 r.touchData.cxt = false;
25461 r.data.bgActivePosistion = undefined;
25462 r.redrawHint('select', true);
25463 var cxtEvt = {
25464 originalEvent: e,
25465 type: 'cxttapend',
25466 position: {
25467 x: now[0],
25468 y: now[1]
25469 }
25470 };
25471
25472 if (r.touchData.start) {
25473 r.touchData.start.unactivate().emit(cxtEvt);
25474 r.touchData.start = null;
25475 } else {
25476 cy.emit(cxtEvt);
25477 }
25478 }
25479 } // context swipe
25480
25481
25482 if (capture && r.touchData.cxt) {
25483 var cxtEvt = {
25484 originalEvent: e,
25485 type: 'cxtdrag',
25486 position: {
25487 x: now[0],
25488 y: now[1]
25489 }
25490 };
25491 r.data.bgActivePosistion = undefined;
25492 r.redrawHint('select', true);
25493
25494 if (r.touchData.start) {
25495 r.touchData.start.emit(cxtEvt);
25496 } else {
25497 cy.emit(cxtEvt);
25498 }
25499
25500 if (r.touchData.start) {
25501 r.touchData.start._private.grabbed = false;
25502 }
25503
25504 r.touchData.cxtDragged = true;
25505 var near = r.findNearestElement(now[0], now[1], true, true);
25506
25507 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25508 if (r.touchData.cxtOver) {
25509 r.touchData.cxtOver.emit({
25510 originalEvent: e,
25511 type: 'cxtdragout',
25512 position: {
25513 x: now[0],
25514 y: now[1]
25515 }
25516 });
25517 }
25518
25519 r.touchData.cxtOver = near;
25520
25521 if (near) {
25522 near.emit({
25523 originalEvent: e,
25524 type: 'cxtdragover',
25525 position: {
25526 x: now[0],
25527 y: now[1]
25528 }
25529 });
25530 }
25531 } // box selection
25532
25533 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25534 e.preventDefault();
25535 r.data.bgActivePosistion = undefined;
25536 this.lastThreeTouch = +new Date();
25537
25538 if (!r.touchData.selecting) {
25539 cy.emit({
25540 originalEvent: e,
25541 type: 'boxstart',
25542 position: {
25543 x: now[0],
25544 y: now[1]
25545 }
25546 });
25547 }
25548
25549 r.touchData.selecting = true;
25550 r.touchData.didSelect = true;
25551 select[4] = 1;
25552
25553 if (!select || select.length === 0 || select[0] === undefined) {
25554 select[0] = (now[0] + now[2] + now[4]) / 3;
25555 select[1] = (now[1] + now[3] + now[5]) / 3;
25556 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25557 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25558 } else {
25559 select[2] = (now[0] + now[2] + now[4]) / 3;
25560 select[3] = (now[1] + now[3] + now[5]) / 3;
25561 }
25562
25563 r.redrawHint('select', true);
25564 r.redraw(); // pinch to zoom
25565 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25566 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25567 // two fingers => pinch to zoom
25568 e.preventDefault();
25569 r.data.bgActivePosistion = undefined;
25570 r.redrawHint('select', true);
25571 var draggedEles = r.dragData.touchDragEles;
25572
25573 if (draggedEles) {
25574 r.redrawHint('drag', true);
25575
25576 for (var i = 0; i < draggedEles.length; i++) {
25577 var de_p = draggedEles[i]._private;
25578 de_p.grabbed = false;
25579 de_p.rscratch.inDragLayer = false;
25580 }
25581 }
25582
25583 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25584
25585 var f1x2 = e.touches[0].clientX - offsetLeft,
25586 f1y2 = e.touches[0].clientY - offsetTop;
25587 var f2x2 = e.touches[1].clientX - offsetLeft,
25588 f2y2 = e.touches[1].clientY - offsetTop;
25589 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25590 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25591
25592 var factor = distance2 / distance1;
25593
25594 if (twoFingersStartInside) {
25595 // delta finger1
25596 var df1x = f1x2 - f1x1;
25597 var df1y = f1y2 - f1y1; // delta finger 2
25598
25599 var df2x = f2x2 - f2x1;
25600 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25601 // i.e. so pinching cancels out and moving together pans
25602
25603 var tx = (df1x + df2x) / 2;
25604 var ty = (df1y + df2y) / 2; // now calculate the zoom
25605
25606 var zoom1 = cy.zoom();
25607 var zoom2 = zoom1 * factor;
25608 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25609
25610 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25611 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25612 var pan2 = {
25613 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25614 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25615 }; // remove dragged eles
25616
25617 if (_start && _start.active()) {
25618 var draggedEles = r.dragData.touchDragEles;
25619 freeDraggedElements(draggedEles);
25620 r.redrawHint('drag', true);
25621 r.redrawHint('eles', true);
25622
25623 _start.unactivate().emit('freeon');
25624
25625 draggedEles.emit('free');
25626
25627 if (r.dragData.didDrag) {
25628 _start.emit('dragfreeon');
25629
25630 draggedEles.emit('dragfree');
25631 }
25632 }
25633
25634 cy.viewport({
25635 zoom: zoom2,
25636 pan: pan2,
25637 cancelOnFailedZoom: true
25638 });
25639 distance1 = distance2;
25640 f1x1 = f1x2;
25641 f1y1 = f1y2;
25642 f2x1 = f2x2;
25643 f2y1 = f2y2;
25644 r.pinching = true;
25645 } // Re-project
25646
25647
25648 if (e.touches[0]) {
25649 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25650 now[0] = pos[0];
25651 now[1] = pos[1];
25652 }
25653
25654 if (e.touches[1]) {
25655 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25656 now[2] = pos[0];
25657 now[3] = pos[1];
25658 }
25659
25660 if (e.touches[2]) {
25661 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25662 now[4] = pos[0];
25663 now[5] = pos[1];
25664 }
25665 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25666 ) {
25667 var start = r.touchData.start;
25668 var last = r.touchData.last;
25669 var near;
25670
25671 if (!r.hoverData.draggingEles && !r.swipePanning) {
25672 near = r.findNearestElement(now[0], now[1], true, true);
25673 }
25674
25675 if (capture && start != null) {
25676 e.preventDefault();
25677 } // dragging nodes
25678
25679
25680 if (capture && start != null && r.nodeIsDraggable(start)) {
25681 if (isOverThresholdDrag) {
25682 // then dragging can happen
25683 var draggedEles = r.dragData.touchDragEles;
25684 var justStartedDrag = !r.dragData.didDrag;
25685
25686 if (justStartedDrag) {
25687 addNodesToDrag(draggedEles, {
25688 inDragLayer: true
25689 });
25690 }
25691
25692 r.dragData.didDrag = true;
25693 var totalShift = {
25694 x: 0,
25695 y: 0
25696 };
25697
25698 if (number(disp[0]) && number(disp[1])) {
25699 totalShift.x += disp[0];
25700 totalShift.y += disp[1];
25701
25702 if (justStartedDrag) {
25703 r.redrawHint('eles', true);
25704 var dragDelta = r.touchData.dragDelta;
25705
25706 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25707 totalShift.x += dragDelta[0];
25708 totalShift.y += dragDelta[1];
25709 }
25710 }
25711 }
25712
25713 r.hoverData.draggingEles = true;
25714 draggedEles.silentShift(totalShift).emit('position drag');
25715 r.redrawHint('drag', true);
25716
25717 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25718 r.redrawHint('eles', true);
25719 }
25720
25721 r.redraw();
25722 } else {
25723 // otherise keep track of drag delta for later
25724 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25725
25726 if (dragDelta.length === 0) {
25727 dragDelta.push(disp[0]);
25728 dragDelta.push(disp[1]);
25729 } else {
25730 dragDelta[0] += disp[0];
25731 dragDelta[1] += disp[1];
25732 }
25733 }
25734 } // touchmove
25735
25736
25737 {
25738 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25739 x: now[0],
25740 y: now[1]
25741 });
25742
25743 if ((!start || !start.grabbed()) && near != last) {
25744 if (last) {
25745 last.emit({
25746 originalEvent: e,
25747 type: 'tapdragout',
25748 position: {
25749 x: now[0],
25750 y: now[1]
25751 }
25752 });
25753 }
25754
25755 if (near) {
25756 near.emit({
25757 originalEvent: e,
25758 type: 'tapdragover',
25759 position: {
25760 x: now[0],
25761 y: now[1]
25762 }
25763 });
25764 }
25765 }
25766
25767 r.touchData.last = near;
25768 } // check to cancel taphold
25769
25770 if (capture) {
25771 for (var i = 0; i < now.length; i++) {
25772 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25773 r.touchData.singleTouchMoved = true;
25774 }
25775 }
25776 } // panning
25777
25778
25779 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25780 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25781
25782 if (allowPassthrough) {
25783 e.preventDefault();
25784
25785 if (!r.data.bgActivePosistion) {
25786 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25787 }
25788
25789 if (r.swipePanning) {
25790 cy.panBy({
25791 x: disp[0] * zoom,
25792 y: disp[1] * zoom
25793 });
25794 } else if (isOverThresholdDrag) {
25795 r.swipePanning = true;
25796 cy.panBy({
25797 x: dx * zoom,
25798 y: dy * zoom
25799 });
25800
25801 if (start) {
25802 start.unactivate();
25803 r.redrawHint('select', true);
25804 r.touchData.start = null;
25805 }
25806 }
25807 } // Re-project
25808
25809
25810 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25811 now[0] = pos[0];
25812 now[1] = pos[1];
25813 }
25814 }
25815
25816 for (var j = 0; j < now.length; j++) {
25817 earlier[j] = now[j];
25818 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25819
25820
25821 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
25822 r.data.bgActivePosistion = undefined;
25823 r.redrawHint('select', true);
25824 r.redraw();
25825 }
25826 }, false);
25827 var touchcancelHandler;
25828 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
25829 // eslint-disable-line no-unused-vars
25830 var start = r.touchData.start;
25831 r.touchData.capture = false;
25832
25833 if (start) {
25834 start.unactivate();
25835 }
25836 });
25837 var touchendHandler;
25838 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
25839 // eslint-disable-line no-unused-vars
25840 var start = r.touchData.start;
25841 var capture = r.touchData.capture;
25842
25843 if (capture) {
25844 if (e.touches.length === 0) {
25845 r.touchData.capture = false;
25846 }
25847
25848 e.preventDefault();
25849 } else {
25850 return;
25851 }
25852
25853 var select = r.selection;
25854 r.swipePanning = false;
25855 r.hoverData.draggingEles = false;
25856 var cy = r.cy;
25857 var zoom = cy.zoom();
25858 var now = r.touchData.now;
25859 var earlier = r.touchData.earlier;
25860
25861 if (e.touches[0]) {
25862 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25863 now[0] = pos[0];
25864 now[1] = pos[1];
25865 }
25866
25867 if (e.touches[1]) {
25868 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25869 now[2] = pos[0];
25870 now[3] = pos[1];
25871 }
25872
25873 if (e.touches[2]) {
25874 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25875 now[4] = pos[0];
25876 now[5] = pos[1];
25877 }
25878
25879 if (start) {
25880 start.unactivate();
25881 }
25882
25883 var ctxTapend;
25884
25885 if (r.touchData.cxt) {
25886 ctxTapend = {
25887 originalEvent: e,
25888 type: 'cxttapend',
25889 position: {
25890 x: now[0],
25891 y: now[1]
25892 }
25893 };
25894
25895 if (start) {
25896 start.emit(ctxTapend);
25897 } else {
25898 cy.emit(ctxTapend);
25899 }
25900
25901 if (!r.touchData.cxtDragged) {
25902 var ctxTap = {
25903 originalEvent: e,
25904 type: 'cxttap',
25905 position: {
25906 x: now[0],
25907 y: now[1]
25908 }
25909 };
25910
25911 if (start) {
25912 start.emit(ctxTap);
25913 } else {
25914 cy.emit(ctxTap);
25915 }
25916 }
25917
25918 if (r.touchData.start) {
25919 r.touchData.start._private.grabbed = false;
25920 }
25921
25922 r.touchData.cxt = false;
25923 r.touchData.start = null;
25924 r.redraw();
25925 return;
25926 } // no more box selection if we don't have three fingers
25927
25928
25929 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
25930 r.touchData.selecting = false;
25931 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25932 select[0] = undefined;
25933 select[1] = undefined;
25934 select[2] = undefined;
25935 select[3] = undefined;
25936 select[4] = 0;
25937 r.redrawHint('select', true);
25938 cy.emit({
25939 type: 'boxend',
25940 originalEvent: e,
25941 position: {
25942 x: now[0],
25943 y: now[1]
25944 }
25945 });
25946
25947 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25948 return ele.selectable() && !ele.selected();
25949 };
25950
25951 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25952
25953 if (box.nonempty()) {
25954 r.redrawHint('eles', true);
25955 }
25956
25957 r.redraw();
25958 }
25959
25960 if (start != null) {
25961 start.unactivate();
25962 }
25963
25964 if (e.touches[2]) {
25965 r.data.bgActivePosistion = undefined;
25966 r.redrawHint('select', true);
25967 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
25968 r.data.bgActivePosistion = undefined;
25969 r.redrawHint('select', true);
25970 var draggedEles = r.dragData.touchDragEles;
25971
25972 if (start != null) {
25973 var startWasGrabbed = start._private.grabbed;
25974 freeDraggedElements(draggedEles);
25975 r.redrawHint('drag', true);
25976 r.redrawHint('eles', true);
25977
25978 if (startWasGrabbed) {
25979 start.emit('freeon');
25980 draggedEles.emit('free');
25981
25982 if (r.dragData.didDrag) {
25983 start.emit('dragfreeon');
25984 draggedEles.emit('dragfree');
25985 }
25986 }
25987
25988 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
25989 x: now[0],
25990 y: now[1]
25991 });
25992 start.unactivate();
25993 r.touchData.start = null;
25994 } else {
25995 var near = r.findNearestElement(now[0], now[1], true, true);
25996 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
25997 x: now[0],
25998 y: now[1]
25999 });
26000 }
26001
26002 var dx = r.touchData.startPosition[0] - now[0];
26003 var dx2 = dx * dx;
26004 var dy = r.touchData.startPosition[1] - now[1];
26005 var dy2 = dy * dy;
26006 var dist2 = dx2 + dy2;
26007 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26008
26009 if (!r.touchData.singleTouchMoved) {
26010 if (!start) {
26011 cy.$(':selected').unselect(['tapunselect']);
26012 }
26013
26014 triggerEvents(start, ['tap', 'vclick'], e, {
26015 x: now[0],
26016 y: now[1]
26017 });
26018 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26019
26020
26021 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26022 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26023 ) {
26024 if (cy.selectionType() === 'single') {
26025 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26026 start.select(['tapselect']);
26027 } else {
26028 if (start.selected()) {
26029 start.unselect(['tapunselect']);
26030 } else {
26031 start.select(['tapselect']);
26032 }
26033 }
26034
26035 r.redrawHint('eles', true);
26036 }
26037
26038 r.touchData.singleTouchMoved = true;
26039 }
26040
26041 for (var j = 0; j < now.length; j++) {
26042 earlier[j] = now[j];
26043 }
26044
26045 r.dragData.didDrag = false; // reset for next touchstart
26046
26047 if (e.touches.length === 0) {
26048 r.touchData.dragDelta = [];
26049 r.touchData.startPosition = null;
26050 r.touchData.startGPosition = null;
26051 r.touchData.didSelect = false;
26052 }
26053
26054 if (e.touches.length < 2) {
26055 if (e.touches.length === 1) {
26056 // the old start global pos'n may not be the same finger that remains
26057 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26058 }
26059
26060 r.pinching = false;
26061 r.redrawHint('eles', true);
26062 r.redraw();
26063 } //r.redraw();
26064
26065 }, false); // fallback compatibility layer for ms pointer events
26066
26067 if (typeof TouchEvent === 'undefined') {
26068 var pointers = [];
26069
26070 var makeTouch = function makeTouch(e) {
26071 return {
26072 clientX: e.clientX,
26073 clientY: e.clientY,
26074 force: 1,
26075 identifier: e.pointerId,
26076 pageX: e.pageX,
26077 pageY: e.pageY,
26078 radiusX: e.width / 2,
26079 radiusY: e.height / 2,
26080 screenX: e.screenX,
26081 screenY: e.screenY,
26082 target: e.target
26083 };
26084 };
26085
26086 var makePointer = function makePointer(e) {
26087 return {
26088 event: e,
26089 touch: makeTouch(e)
26090 };
26091 };
26092
26093 var addPointer = function addPointer(e) {
26094 pointers.push(makePointer(e));
26095 };
26096
26097 var removePointer = function removePointer(e) {
26098 for (var i = 0; i < pointers.length; i++) {
26099 var p = pointers[i];
26100
26101 if (p.event.pointerId === e.pointerId) {
26102 pointers.splice(i, 1);
26103 return;
26104 }
26105 }
26106 };
26107
26108 var updatePointer = function updatePointer(e) {
26109 var p = pointers.filter(function (p) {
26110 return p.event.pointerId === e.pointerId;
26111 })[0];
26112 p.event = e;
26113 p.touch = makeTouch(e);
26114 };
26115
26116 var addTouchesToEvent = function addTouchesToEvent(e) {
26117 e.touches = pointers.map(function (p) {
26118 return p.touch;
26119 });
26120 };
26121
26122 var pointerIsMouse = function pointerIsMouse(e) {
26123 return e.pointerType === 'mouse' || e.pointerType === 4;
26124 };
26125
26126 r.registerBinding(r.container, 'pointerdown', function (e) {
26127 if (pointerIsMouse(e)) {
26128 return;
26129 } // mouse already handled
26130
26131
26132 e.preventDefault();
26133 addPointer(e);
26134 addTouchesToEvent(e);
26135 touchstartHandler(e);
26136 });
26137 r.registerBinding(r.container, 'pointerup', function (e) {
26138 if (pointerIsMouse(e)) {
26139 return;
26140 } // mouse already handled
26141
26142
26143 removePointer(e);
26144 addTouchesToEvent(e);
26145 touchendHandler(e);
26146 });
26147 r.registerBinding(r.container, 'pointercancel', function (e) {
26148 if (pointerIsMouse(e)) {
26149 return;
26150 } // mouse already handled
26151
26152
26153 removePointer(e);
26154 addTouchesToEvent(e);
26155 touchcancelHandler(e);
26156 });
26157 r.registerBinding(r.container, 'pointermove', function (e) {
26158 if (pointerIsMouse(e)) {
26159 return;
26160 } // mouse already handled
26161
26162
26163 e.preventDefault();
26164 updatePointer(e);
26165 addTouchesToEvent(e);
26166 touchmoveHandler(e);
26167 });
26168 }
26169};
26170
26171var BRp$d = {};
26172
26173BRp$d.generatePolygon = function (name, points) {
26174 return this.nodeShapes[name] = {
26175 renderer: this,
26176 name: name,
26177 points: points,
26178 draw: function draw(context, centerX, centerY, width, height) {
26179 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26180 },
26181 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26182 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26183 },
26184 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26185 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26186 }
26187 };
26188};
26189
26190BRp$d.generateEllipse = function () {
26191 return this.nodeShapes['ellipse'] = {
26192 renderer: this,
26193 name: 'ellipse',
26194 draw: function draw(context, centerX, centerY, width, height) {
26195 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26196 },
26197 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26198 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26199 },
26200 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26201 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26202 }
26203 };
26204};
26205
26206BRp$d.generateRoundPolygon = function (name, points) {
26207 // Pre-compute control points
26208 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26209 // the unit vectors.
26210 // For simplicity the layout will be:
26211 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26212 var allPoints = new Array(points.length * 2);
26213
26214 for (var i = 0; i < points.length / 2; i++) {
26215 var sourceIndex = i * 2;
26216 var destIndex = void 0;
26217
26218 if (i < points.length / 2 - 1) {
26219 destIndex = (i + 1) * 2;
26220 } else {
26221 destIndex = 0;
26222 }
26223
26224 allPoints[i * 4] = points[sourceIndex];
26225 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26226 var xDest = points[destIndex] - points[sourceIndex];
26227 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26228 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26229 allPoints[i * 4 + 2] = xDest / norm;
26230 allPoints[i * 4 + 3] = yDest / norm;
26231 }
26232
26233 return this.nodeShapes[name] = {
26234 renderer: this,
26235 name: name,
26236 points: allPoints,
26237 draw: function draw(context, centerX, centerY, width, height) {
26238 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26239 },
26240 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26241 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26242 },
26243 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26244 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26245 }
26246 };
26247};
26248
26249BRp$d.generateRoundRectangle = function () {
26250 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26251 renderer: this,
26252 name: 'round-rectangle',
26253 points: generateUnitNgonPointsFitToSquare(4, 0),
26254 draw: function draw(context, centerX, centerY, width, height) {
26255 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26256 },
26257 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26258 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26259 },
26260 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26261 var cornerRadius = getRoundRectangleRadius(width, height);
26262 var diam = cornerRadius * 2; // Check hBox
26263
26264 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26265 return true;
26266 } // Check vBox
26267
26268
26269 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26270 return true;
26271 } // Check top left quarter circle
26272
26273
26274 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26275 return true;
26276 } // Check top right quarter circle
26277
26278
26279 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26280 return true;
26281 } // Check bottom right quarter circle
26282
26283
26284 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26285 return true;
26286 } // Check bottom left quarter circle
26287
26288
26289 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26290 return true;
26291 }
26292
26293 return false;
26294 }
26295 };
26296};
26297
26298BRp$d.generateCutRectangle = function () {
26299 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26300 renderer: this,
26301 name: 'cut-rectangle',
26302 cornerLength: getCutRectangleCornerLength(),
26303 points: generateUnitNgonPointsFitToSquare(4, 0),
26304 draw: function draw(context, centerX, centerY, width, height) {
26305 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26306 },
26307 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
26308 var cl = this.cornerLength;
26309 var hh = height / 2;
26310 var hw = width / 2;
26311 var xBegin = centerX - hw;
26312 var xEnd = centerX + hw;
26313 var yBegin = centerY - hh;
26314 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26315
26316 return {
26317 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26318 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26319 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26320 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26321 };
26322 },
26323 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26324 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26325 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26326 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26327 },
26328 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26329 // Check hBox
26330 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
26331 return true;
26332 } // Check vBox
26333
26334
26335 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
26336 return true;
26337 }
26338
26339 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26340 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26341 }
26342 };
26343};
26344
26345BRp$d.generateBarrel = function () {
26346 return this.nodeShapes['barrel'] = {
26347 renderer: this,
26348 name: 'barrel',
26349 points: generateUnitNgonPointsFitToSquare(4, 0),
26350 draw: function draw(context, centerX, centerY, width, height) {
26351 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26352 },
26353 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26354 // use two fixed t values for the bezier curve approximation
26355 var t0 = 0.15;
26356 var t1 = 0.5;
26357 var t2 = 0.85;
26358 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26359
26360 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26361 // approximate curve pts based on the two t values
26362 var m0 = qbezierPtAt({
26363 x: pts[0],
26364 y: pts[1]
26365 }, {
26366 x: pts[2],
26367 y: pts[3]
26368 }, {
26369 x: pts[4],
26370 y: pts[5]
26371 }, t0);
26372 var m1 = qbezierPtAt({
26373 x: pts[0],
26374 y: pts[1]
26375 }, {
26376 x: pts[2],
26377 y: pts[3]
26378 }, {
26379 x: pts[4],
26380 y: pts[5]
26381 }, t1);
26382 var m2 = qbezierPtAt({
26383 x: pts[0],
26384 y: pts[1]
26385 }, {
26386 x: pts[2],
26387 y: pts[3]
26388 }, {
26389 x: pts[4],
26390 y: pts[5]
26391 }, t2);
26392 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26393 };
26394
26395 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26396 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26397 },
26398 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26399 var hh = height / 2;
26400 var hw = width / 2;
26401 var xBegin = centerX - hw;
26402 var xEnd = centerX + hw;
26403 var yBegin = centerY - hh;
26404 var yEnd = centerY + hh;
26405 var curveConstants = getBarrelCurveConstants(width, height);
26406 var hOffset = curveConstants.heightOffset;
26407 var wOffset = curveConstants.widthOffset;
26408 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26409
26410 var pts = {
26411 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26412 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26413 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26414 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26415 };
26416 pts.topLeft.isTop = true;
26417 pts.topRight.isTop = true;
26418 pts.bottomLeft.isBottom = true;
26419 pts.bottomRight.isBottom = true;
26420 return pts;
26421 },
26422 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26423 var curveConstants = getBarrelCurveConstants(width, height);
26424 var hOffset = curveConstants.heightOffset;
26425 var wOffset = curveConstants.widthOffset; // Check hBox
26426
26427 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26428 return true;
26429 } // Check vBox
26430
26431
26432 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26433 return true;
26434 }
26435
26436 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26437
26438 var getCurveT = function getCurveT(x, y, curvePts) {
26439 var x0 = curvePts[4];
26440 var x1 = curvePts[2];
26441 var x2 = curvePts[0];
26442 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26443
26444 var y2 = curvePts[1];
26445 var xMin = Math.min(x0, x2);
26446 var xMax = Math.max(x0, x2);
26447 var yMin = Math.min(y0, y2);
26448 var yMax = Math.max(y0, y2);
26449
26450 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26451 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26452 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26453 var validRoots = roots.filter(function (r) {
26454 return 0 <= r && r <= 1;
26455 });
26456
26457 if (validRoots.length > 0) {
26458 return validRoots[0];
26459 }
26460 }
26461
26462 return null;
26463 };
26464
26465 var curveRegions = Object.keys(barrelCurvePts);
26466
26467 for (var i = 0; i < curveRegions.length; i++) {
26468 var corner = curveRegions[i];
26469 var cornerPts = barrelCurvePts[corner];
26470 var t = getCurveT(x, y, cornerPts);
26471
26472 if (t == null) {
26473 continue;
26474 }
26475
26476 var y0 = cornerPts[5];
26477 var y1 = cornerPts[3];
26478 var y2 = cornerPts[1];
26479 var bezY = qbezierAt(y0, y1, y2, t);
26480
26481 if (cornerPts.isTop && bezY <= y) {
26482 return true;
26483 }
26484
26485 if (cornerPts.isBottom && y <= bezY) {
26486 return true;
26487 }
26488 }
26489
26490 return false;
26491 }
26492 };
26493};
26494
26495BRp$d.generateBottomRoundrectangle = function () {
26496 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26497 renderer: this,
26498 name: 'bottom-round-rectangle',
26499 points: generateUnitNgonPointsFitToSquare(4, 0),
26500 draw: function draw(context, centerX, centerY, width, height) {
26501 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26502 },
26503 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26504 var topStartX = nodeX - (width / 2 + padding);
26505 var topStartY = nodeY - (height / 2 + padding);
26506 var topEndY = topStartY;
26507 var topEndX = nodeX + (width / 2 + padding);
26508 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26509
26510 if (topIntersections.length > 0) {
26511 return topIntersections;
26512 }
26513
26514 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26515 },
26516 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26517 var cornerRadius = getRoundRectangleRadius(width, height);
26518 var diam = 2 * cornerRadius; // Check hBox
26519
26520 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26521 return true;
26522 } // Check vBox
26523
26524
26525 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26526 return true;
26527 } // check non-rounded top side
26528
26529
26530 var outerWidth = width / 2 + 2 * padding;
26531 var outerHeight = height / 2 + 2 * padding;
26532 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26533
26534 if (pointInsidePolygonPoints(x, y, points)) {
26535 return true;
26536 } // Check bottom right quarter circle
26537
26538
26539 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26540 return true;
26541 } // Check bottom left quarter circle
26542
26543
26544 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26545 return true;
26546 }
26547
26548 return false;
26549 }
26550 };
26551};
26552
26553BRp$d.registerNodeShapes = function () {
26554 var nodeShapes = this.nodeShapes = {};
26555 var renderer = this;
26556 this.generateEllipse();
26557 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26558 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26559 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26560 nodeShapes['square'] = nodeShapes['rectangle'];
26561 this.generateRoundRectangle();
26562 this.generateCutRectangle();
26563 this.generateBarrel();
26564 this.generateBottomRoundrectangle();
26565 {
26566 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26567 this.generatePolygon('diamond', diamondPoints);
26568 this.generateRoundPolygon('round-diamond', diamondPoints);
26569 }
26570 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26571 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26572 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26573 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26574 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26575 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26576 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26577 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26578 var star5Points = new Array(20);
26579 {
26580 var outerPoints = generateUnitNgonPoints(5, 0);
26581 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26582
26583 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26584 innerRadius *= 1.57;
26585
26586 for (var i = 0; i < innerPoints.length / 2; i++) {
26587 innerPoints[i * 2] *= innerRadius;
26588 innerPoints[i * 2 + 1] *= innerRadius;
26589 }
26590
26591 for (var i = 0; i < 20 / 4; i++) {
26592 star5Points[i * 4] = outerPoints[i * 2];
26593 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26594 star5Points[i * 4 + 2] = innerPoints[i * 2];
26595 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26596 }
26597 }
26598 star5Points = fitPolygonToSquare(star5Points);
26599 this.generatePolygon('star', star5Points);
26600 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26601 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26602 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]);
26603 {
26604 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26605 this.generatePolygon('tag', tagPoints);
26606 this.generateRoundPolygon('round-tag', tagPoints);
26607 }
26608
26609 nodeShapes.makePolygon = function (points) {
26610 // use caching on user-specified polygons so they are as fast as native shapes
26611 var key = points.join('$');
26612 var name = 'polygon-' + key;
26613 var shape;
26614
26615 if (shape = this[name]) {
26616 // got cached shape
26617 return shape;
26618 } // create and cache new shape
26619
26620
26621 return renderer.generatePolygon(name, points);
26622 };
26623};
26624
26625var BRp$e = {};
26626
26627BRp$e.timeToRender = function () {
26628 return this.redrawTotalTime / this.redrawCount;
26629};
26630
26631BRp$e.redraw = function (options) {
26632 options = options || staticEmptyObject();
26633 var r = this;
26634
26635 if (r.averageRedrawTime === undefined) {
26636 r.averageRedrawTime = 0;
26637 }
26638
26639 if (r.lastRedrawTime === undefined) {
26640 r.lastRedrawTime = 0;
26641 }
26642
26643 if (r.lastDrawTime === undefined) {
26644 r.lastDrawTime = 0;
26645 }
26646
26647 r.requestedFrame = true;
26648 r.renderOptions = options;
26649};
26650
26651BRp$e.beforeRender = function (fn, priority) {
26652 // the renderer can't add tick callbacks when destroyed
26653 if (this.destroyed) {
26654 return;
26655 }
26656
26657 if (priority == null) {
26658 error('Priority is not optional for beforeRender');
26659 }
26660
26661 var cbs = this.beforeRenderCallbacks;
26662 cbs.push({
26663 fn: fn,
26664 priority: priority
26665 }); // higher priority callbacks executed first
26666
26667 cbs.sort(function (a, b) {
26668 return b.priority - a.priority;
26669 });
26670};
26671
26672var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26673 var cbs = r.beforeRenderCallbacks;
26674
26675 for (var i = 0; i < cbs.length; i++) {
26676 cbs[i].fn(willDraw, startTime);
26677 }
26678};
26679
26680BRp$e.startRenderLoop = function () {
26681 var r = this;
26682 var cy = r.cy;
26683
26684 if (r.renderLoopStarted) {
26685 return;
26686 } else {
26687 r.renderLoopStarted = true;
26688 }
26689
26690 var renderFn = function renderFn(requestTime) {
26691 if (r.destroyed) {
26692 return;
26693 }
26694
26695 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26696 beforeRenderCallbacks(r, true, requestTime);
26697 var startTime = performanceNow();
26698 r.render(r.renderOptions);
26699 var endTime = r.lastDrawTime = performanceNow();
26700
26701 if (r.averageRedrawTime === undefined) {
26702 r.averageRedrawTime = endTime - startTime;
26703 }
26704
26705 if (r.redrawCount === undefined) {
26706 r.redrawCount = 0;
26707 }
26708
26709 r.redrawCount++;
26710
26711 if (r.redrawTotalTime === undefined) {
26712 r.redrawTotalTime = 0;
26713 }
26714
26715 var duration = endTime - startTime;
26716 r.redrawTotalTime += duration;
26717 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26718
26719 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26720 r.requestedFrame = false;
26721 } else {
26722 beforeRenderCallbacks(r, false, requestTime);
26723 }
26724
26725 r.skipFrame = false;
26726 requestAnimationFrame(renderFn);
26727 };
26728
26729 requestAnimationFrame(renderFn);
26730};
26731
26732var BaseRenderer = function BaseRenderer(options) {
26733 this.init(options);
26734};
26735
26736var BR = BaseRenderer;
26737var BRp$f = BR.prototype;
26738BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26739
26740BRp$f.init = function (options) {
26741 var r = this;
26742 r.options = options;
26743 r.cy = options.cy;
26744 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26745
26746 if (window$1) {
26747 var document = window$1.document;
26748 var head = document.head;
26749 var stylesheetId = '__________cytoscape_stylesheet';
26750 var className = '__________cytoscape_container';
26751 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26752
26753 if (ctr.className.indexOf(className) < 0) {
26754 ctr.className = (ctr.className || '') + ' ' + className;
26755 }
26756
26757 if (!stylesheetAlreadyExists) {
26758 var stylesheet = document.createElement('style');
26759 stylesheet.id = stylesheetId;
26760 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26761 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26762 }
26763
26764 var computedStyle = window$1.getComputedStyle(ctr);
26765 var position = computedStyle.getPropertyValue('position');
26766
26767 if (position === 'static') {
26768 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26769 }
26770 }
26771
26772 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26773
26774 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
26775
26776 r.hoverData = {
26777 down: null,
26778 last: null,
26779 downTime: null,
26780 triggerMode: null,
26781 dragging: false,
26782 initialPan: [null, null],
26783 capture: false
26784 };
26785 r.dragData = {
26786 possibleDragElements: []
26787 };
26788 r.touchData = {
26789 start: null,
26790 capture: false,
26791 // These 3 fields related to tap, taphold events
26792 startPosition: [null, null, null, null, null, null],
26793 singleTouchStartTime: null,
26794 singleTouchMoved: true,
26795 now: [null, null, null, null, null, null],
26796 earlier: [null, null, null, null, null, null]
26797 };
26798 r.redraws = 0;
26799 r.showFps = options.showFps;
26800 r.debug = options.debug;
26801 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
26802 r.textureOnViewport = options.textureOnViewport;
26803 r.wheelSensitivity = options.wheelSensitivity;
26804 r.motionBlurEnabled = options.motionBlur; // on by default
26805
26806 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
26807 r.motionBlur = options.motionBlur; // for initial kick off
26808
26809 r.motionBlurOpacity = options.motionBlurOpacity;
26810 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
26811 r.motionBlurPxRatio = 1;
26812 r.mbPxRBlurry = 1; //0.8;
26813
26814 r.minMbLowQualFrames = 4;
26815 r.fullQualityMb = false;
26816 r.clearedForMotionBlur = [];
26817 r.desktopTapThreshold = options.desktopTapThreshold;
26818 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
26819 r.touchTapThreshold = options.touchTapThreshold;
26820 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
26821 r.tapholdDuration = 500;
26822 r.bindings = [];
26823 r.beforeRenderCallbacks = [];
26824 r.beforeRenderPriorities = {
26825 // higher priority execs before lower one
26826 animations: 400,
26827 eleCalcs: 300,
26828 eleTxrDeq: 200,
26829 lyrTxrDeq: 150,
26830 lyrTxrSkip: 100
26831 };
26832 r.registerNodeShapes();
26833 r.registerArrowShapes();
26834 r.registerCalculationListeners();
26835};
26836
26837BRp$f.notify = function (eventName, eles) {
26838 var r = this;
26839 var cy = r.cy; // the renderer can't be notified after it's destroyed
26840
26841 if (this.destroyed) {
26842 return;
26843 }
26844
26845 if (eventName === 'init') {
26846 r.load();
26847 return;
26848 }
26849
26850 if (eventName === 'destroy') {
26851 r.destroy();
26852 return;
26853 }
26854
26855 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
26856 r.invalidateCachedZSortedEles();
26857 }
26858
26859 if (eventName === 'viewport') {
26860 r.redrawHint('select', true);
26861 }
26862
26863 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
26864 r.invalidateContainerClientCoordsCache();
26865 r.matchCanvasSize(r.container);
26866 }
26867
26868 r.redrawHint('eles', true);
26869 r.redrawHint('drag', true);
26870 this.startRenderLoop();
26871 this.redraw();
26872};
26873
26874BRp$f.destroy = function () {
26875 var r = this;
26876 r.destroyed = true;
26877 r.cy.stopAnimationLoop();
26878
26879 for (var i = 0; i < r.bindings.length; i++) {
26880 var binding = r.bindings[i];
26881 var b = binding;
26882 var tgt = b.target;
26883 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
26884 }
26885
26886 r.bindings = [];
26887 r.beforeRenderCallbacks = [];
26888 r.onUpdateEleCalcsFns = [];
26889
26890 if (r.removeObserver) {
26891 r.removeObserver.disconnect();
26892 }
26893
26894 if (r.styleObserver) {
26895 r.styleObserver.disconnect();
26896 }
26897
26898 if (r.resizeObserver) {
26899 r.resizeObserver.disconnect();
26900 }
26901
26902 if (r.labelCalcDiv) {
26903 try {
26904 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
26905 } catch (e) {// ie10 issue #1014
26906 }
26907 }
26908};
26909
26910BRp$f.isHeadless = function () {
26911 return false;
26912};
26913
26914[BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
26915 extend(BRp$f, props);
26916});
26917
26918var fullFpsTime = 1000 / 60; // assume 60 frames per second
26919
26920var defs = {
26921 setupDequeueing: function setupDequeueing(opts) {
26922 return function setupDequeueingImpl() {
26923 var self = this;
26924 var r = this.renderer;
26925
26926 if (self.dequeueingSetup) {
26927 return;
26928 } else {
26929 self.dequeueingSetup = true;
26930 }
26931
26932 var queueRedraw = util(function () {
26933 r.redrawHint('eles', true);
26934 r.redrawHint('drag', true);
26935 r.redraw();
26936 }, opts.deqRedrawThreshold);
26937
26938 var dequeue = function dequeue(willDraw, frameStartTime) {
26939 var startTime = performanceNow();
26940 var avgRenderTime = r.averageRedrawTime;
26941 var renderTime = r.lastRedrawTime;
26942 var deqd = [];
26943 var extent = r.cy.extent();
26944 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
26945 // queue won't automatically be flushed before dequeueing starts
26946
26947 if (!willDraw) {
26948 r.flushRenderedStyleQueue();
26949 }
26950
26951 while (true) {
26952 // eslint-disable-line no-constant-condition
26953 var now = performanceNow();
26954 var duration = now - startTime;
26955 var frameDuration = now - frameStartTime;
26956
26957 if (renderTime < fullFpsTime) {
26958 // if we're rendering faster than the ideal fps, then do dequeueing
26959 // during all of the remaining frame time
26960 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
26961
26962 if (frameDuration >= opts.deqFastCost * timeAvailable) {
26963 break;
26964 }
26965 } else {
26966 if (willDraw) {
26967 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
26968 break;
26969 }
26970 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
26971 break;
26972 }
26973 }
26974
26975 var thisDeqd = opts.deq(self, pixelRatio, extent);
26976
26977 if (thisDeqd.length > 0) {
26978 for (var i = 0; i < thisDeqd.length; i++) {
26979 deqd.push(thisDeqd[i]);
26980 }
26981 } else {
26982 break;
26983 }
26984 } // callbacks on dequeue
26985
26986
26987 if (deqd.length > 0) {
26988 opts.onDeqd(self, deqd);
26989
26990 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
26991 queueRedraw();
26992 }
26993 }
26994 };
26995
26996 var priority = opts.priority || noop;
26997 r.beforeRender(dequeue, priority(self));
26998 };
26999 }
27000};
27001
27002// Uses keys so elements may share the same cache.
27003
27004var ElementTextureCacheLookup =
27005/*#__PURE__*/
27006function () {
27007 function ElementTextureCacheLookup(getKey) {
27008 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27009
27010 _classCallCheck(this, ElementTextureCacheLookup);
27011
27012 this.idsByKey = new Map$1();
27013 this.keyForId = new Map$1();
27014 this.cachesByLvl = new Map$1();
27015 this.lvls = [];
27016 this.getKey = getKey;
27017 this.doesEleInvalidateKey = doesEleInvalidateKey;
27018 }
27019
27020 _createClass(ElementTextureCacheLookup, [{
27021 key: "getIdsFor",
27022 value: function getIdsFor(key) {
27023 if (key == null) {
27024 error("Can not get id list for null key");
27025 }
27026
27027 var idsByKey = this.idsByKey;
27028 var ids = this.idsByKey.get(key);
27029
27030 if (!ids) {
27031 ids = new Set$1();
27032 idsByKey.set(key, ids);
27033 }
27034
27035 return ids;
27036 }
27037 }, {
27038 key: "addIdForKey",
27039 value: function addIdForKey(key, id) {
27040 if (key != null) {
27041 this.getIdsFor(key).add(id);
27042 }
27043 }
27044 }, {
27045 key: "deleteIdForKey",
27046 value: function deleteIdForKey(key, id) {
27047 if (key != null) {
27048 this.getIdsFor(key)["delete"](id);
27049 }
27050 }
27051 }, {
27052 key: "getNumberOfIdsForKey",
27053 value: function getNumberOfIdsForKey(key) {
27054 if (key == null) {
27055 return 0;
27056 } else {
27057 return this.getIdsFor(key).size;
27058 }
27059 }
27060 }, {
27061 key: "updateKeyMappingFor",
27062 value: function updateKeyMappingFor(ele) {
27063 var id = ele.id();
27064 var prevKey = this.keyForId.get(id);
27065 var currKey = this.getKey(ele);
27066 this.deleteIdForKey(prevKey, id);
27067 this.addIdForKey(currKey, id);
27068 this.keyForId.set(id, currKey);
27069 }
27070 }, {
27071 key: "deleteKeyMappingFor",
27072 value: function deleteKeyMappingFor(ele) {
27073 var id = ele.id();
27074 var prevKey = this.keyForId.get(id);
27075 this.deleteIdForKey(prevKey, id);
27076 this.keyForId["delete"](id);
27077 }
27078 }, {
27079 key: "keyHasChangedFor",
27080 value: function keyHasChangedFor(ele) {
27081 var id = ele.id();
27082 var prevKey = this.keyForId.get(id);
27083 var newKey = this.getKey(ele);
27084 return prevKey !== newKey;
27085 }
27086 }, {
27087 key: "isInvalid",
27088 value: function isInvalid(ele) {
27089 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27090 }
27091 }, {
27092 key: "getCachesAt",
27093 value: function getCachesAt(lvl) {
27094 var cachesByLvl = this.cachesByLvl,
27095 lvls = this.lvls;
27096 var caches = cachesByLvl.get(lvl);
27097
27098 if (!caches) {
27099 caches = new Map$1();
27100 cachesByLvl.set(lvl, caches);
27101 lvls.push(lvl);
27102 }
27103
27104 return caches;
27105 }
27106 }, {
27107 key: "getCache",
27108 value: function getCache(key, lvl) {
27109 return this.getCachesAt(lvl).get(key);
27110 }
27111 }, {
27112 key: "get",
27113 value: function get(ele, lvl) {
27114 var key = this.getKey(ele);
27115 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27116
27117 if (cache != null) {
27118 this.updateKeyMappingFor(ele);
27119 }
27120
27121 return cache;
27122 }
27123 }, {
27124 key: "getForCachedKey",
27125 value: function getForCachedKey(ele, lvl) {
27126 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27127
27128 var cache = this.getCache(key, lvl);
27129 return cache;
27130 }
27131 }, {
27132 key: "hasCache",
27133 value: function hasCache(key, lvl) {
27134 return this.getCachesAt(lvl).has(key);
27135 }
27136 }, {
27137 key: "has",
27138 value: function has(ele, lvl) {
27139 var key = this.getKey(ele);
27140 return this.hasCache(key, lvl);
27141 }
27142 }, {
27143 key: "setCache",
27144 value: function setCache(key, lvl, cache) {
27145 cache.key = key;
27146 this.getCachesAt(lvl).set(key, cache);
27147 }
27148 }, {
27149 key: "set",
27150 value: function set(ele, lvl, cache) {
27151 var key = this.getKey(ele);
27152 this.setCache(key, lvl, cache);
27153 this.updateKeyMappingFor(ele);
27154 }
27155 }, {
27156 key: "deleteCache",
27157 value: function deleteCache(key, lvl) {
27158 this.getCachesAt(lvl)["delete"](key);
27159 }
27160 }, {
27161 key: "delete",
27162 value: function _delete(ele, lvl) {
27163 var key = this.getKey(ele);
27164 this.deleteCache(key, lvl);
27165 }
27166 }, {
27167 key: "invalidateKey",
27168 value: function invalidateKey(key) {
27169 var _this = this;
27170
27171 this.lvls.forEach(function (lvl) {
27172 return _this.deleteCache(key, lvl);
27173 });
27174 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27175
27176 }, {
27177 key: "invalidate",
27178 value: function invalidate(ele) {
27179 var id = ele.id();
27180 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27181
27182 this.deleteKeyMappingFor(ele);
27183 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27184
27185 if (entireKeyInvalidated) {
27186 // clear mapping for current key
27187 this.invalidateKey(key);
27188 }
27189
27190 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27191 }
27192 }]);
27193
27194 return ElementTextureCacheLookup;
27195}();
27196
27197var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27198
27199var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27200
27201var minLvl = -4; // when scaling smaller than that we don't need to re-render
27202
27203var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27204
27205var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27206
27207var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27208
27209var defTxrWidth = 1024; // default/minimum texture width
27210
27211var maxTxrW = 1024; // the maximum width of a texture
27212
27213var maxTxrH = 1024; // the maximum height of a texture
27214
27215var minUtility = 0.2; // if usage of texture is less than this, it is retired
27216
27217var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27218
27219var maxFullnessChecks = 10; // dequeued after this many checks
27220
27221var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27222
27223var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27224
27225var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27226
27227var deqFastCost = 0.9; // % of frame time to be used when >60fps
27228
27229var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27230
27231var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27232
27233var getTxrReasons = {
27234 dequeue: 'dequeue',
27235 downscale: 'downscale',
27236 highQuality: 'highQuality'
27237};
27238var initDefaults = defaults({
27239 getKey: null,
27240 doesEleInvalidateKey: falsify,
27241 drawElement: null,
27242 getBoundingBox: null,
27243 getRotationPoint: null,
27244 getRotationOffset: null,
27245 isVisible: trueify,
27246 allowEdgeTxrCaching: true,
27247 allowParentTxrCaching: true
27248});
27249
27250var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27251 var self = this;
27252 self.renderer = renderer;
27253 self.onDequeues = [];
27254 var opts = initDefaults(initOptions);
27255 extend(self, opts);
27256 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27257 self.setupDequeueing();
27258};
27259
27260var ETCp = ElementTextureCache.prototype;
27261ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27262
27263ETCp.getTextureQueue = function (txrH) {
27264 var self = this;
27265 self.eleImgCaches = self.eleImgCaches || {};
27266 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27267}; // the list of usused textures which can be recycled (in use in texture queue)
27268
27269
27270ETCp.getRetiredTextureQueue = function (txrH) {
27271 var self = this;
27272 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27273 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27274 return rtxtrQ;
27275}; // queue of element draw requests at different scale levels
27276
27277
27278ETCp.getElementQueue = function () {
27279 var self = this;
27280 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
27281 return b.reqs - a.reqs;
27282 });
27283 return q;
27284}; // queue of element draw requests at different scale levels (element id lookup)
27285
27286
27287ETCp.getElementKeyToQueue = function () {
27288 var self = this;
27289 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27290 return k2q;
27291};
27292
27293ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27294 var self = this;
27295 var r = this.renderer;
27296 var zoom = r.cy.zoom();
27297 var lookup = this.lookup;
27298
27299 if (bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible()) {
27300 return null;
27301 }
27302
27303 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27304 return null;
27305 }
27306
27307 if (lvl == null) {
27308 lvl = Math.ceil(log2(zoom * pxRatio));
27309 }
27310
27311 if (lvl < minLvl) {
27312 lvl = minLvl;
27313 } else if (zoom >= maxZoom || lvl > maxLvl) {
27314 return null;
27315 }
27316
27317 var scale = Math.pow(2, lvl);
27318 var eleScaledH = bb.h * scale;
27319 var eleScaledW = bb.w * scale;
27320 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27321
27322 if (!this.isVisible(ele, scaledLabelShown)) {
27323 return null;
27324 }
27325
27326 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
27327
27328 if (eleCache && eleCache.invalidated) {
27329 eleCache.invalidated = false;
27330 eleCache.texture.invalidatedWidth -= eleCache.width;
27331 }
27332
27333 if (eleCache) {
27334 return eleCache;
27335 }
27336
27337 var txrH; // which texture height this ele belongs to
27338
27339 if (eleScaledH <= minTxrH) {
27340 txrH = minTxrH;
27341 } else if (eleScaledH <= txrStepH) {
27342 txrH = txrStepH;
27343 } else {
27344 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27345 }
27346
27347 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27348 return null; // caching large elements is not efficient
27349 }
27350
27351 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
27352
27353 var txr = txrQ[txrQ.length - 2];
27354
27355 var addNewTxr = function addNewTxr() {
27356 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27357 }; // try the last one if there is no second last one
27358
27359
27360 if (!txr) {
27361 txr = txrQ[txrQ.length - 1];
27362 } // if the last one doesn't exist, we need a first one
27363
27364
27365 if (!txr) {
27366 txr = addNewTxr();
27367 } // if there's no room in the current texture, we need a new one
27368
27369
27370 if (txr.width - txr.usedWidth < eleScaledW) {
27371 txr = addNewTxr();
27372 }
27373
27374 var scalableFrom = function scalableFrom(otherCache) {
27375 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27376 };
27377
27378 var deqing = reason && reason === getTxrReasons.dequeue;
27379 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27380 var downscaleReq = reason && reason === getTxrReasons.downscale;
27381 var higherCache; // the nearest cache with a higher level
27382
27383 for (var l = lvl + 1; l <= maxLvl; l++) {
27384 var c = lookup.get(ele, l);
27385
27386 if (c) {
27387 higherCache = c;
27388 break;
27389 }
27390 }
27391
27392 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27393
27394 var downscale = function downscale() {
27395 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27396 }; // reset ele area in texture
27397
27398
27399 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27400 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27401
27402 if (scalableFrom(oneUpCache)) {
27403 // then we can relatively cheaply rescale the existing image w/o rerendering
27404 downscale();
27405 } else if (scalableFrom(higherCache)) {
27406 // then use the higher cache for now and queue the next level down
27407 // to cheaply scale towards the smaller level
27408 if (highQualityReq) {
27409 for (var _l = higherCache.level; _l > lvl; _l--) {
27410 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27411 }
27412
27413 downscale();
27414 } else {
27415 self.queueElement(ele, higherCache.level - 1);
27416 return higherCache;
27417 }
27418 } else {
27419 var lowerCache; // the nearest cache with a lower level
27420
27421 if (!deqing && !highQualityReq && !downscaleReq) {
27422 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
27423 var _c = lookup.get(ele, _l2);
27424
27425 if (_c) {
27426 lowerCache = _c;
27427 break;
27428 }
27429 }
27430 }
27431
27432 if (scalableFrom(lowerCache)) {
27433 // then use the lower quality cache for now and queue the better one for later
27434 self.queueElement(ele, lvl);
27435 return lowerCache;
27436 }
27437
27438 txr.context.translate(txr.usedWidth, 0);
27439 txr.context.scale(scale, scale);
27440 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27441 txr.context.scale(1 / scale, 1 / scale);
27442 txr.context.translate(-txr.usedWidth, 0);
27443 }
27444
27445 eleCache = {
27446 x: txr.usedWidth,
27447 texture: txr,
27448 level: lvl,
27449 scale: scale,
27450 width: eleScaledW,
27451 height: eleScaledH,
27452 scaledLabelShown: scaledLabelShown
27453 };
27454 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27455 txr.eleCaches.push(eleCache);
27456 lookup.set(ele, lvl, eleCache);
27457 self.checkTextureFullness(txr);
27458 return eleCache;
27459};
27460
27461ETCp.invalidateElements = function (eles) {
27462 for (var i = 0; i < eles.length; i++) {
27463 this.invalidateElement(eles[i]);
27464 }
27465};
27466
27467ETCp.invalidateElement = function (ele) {
27468 var self = this;
27469 var lookup = self.lookup;
27470 var caches = [];
27471 var invalid = lookup.isInvalid(ele);
27472
27473 if (!invalid) {
27474 return; // override the invalidation request if the element key has not changed
27475 }
27476
27477 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
27478 var cache = lookup.getForCachedKey(ele, lvl);
27479
27480 if (cache) {
27481 caches.push(cache);
27482 }
27483 }
27484
27485 var noOtherElesUseCache = lookup.invalidate(ele);
27486
27487 if (noOtherElesUseCache) {
27488 for (var i = 0; i < caches.length; i++) {
27489 var _cache = caches[i];
27490 var txr = _cache.texture; // remove space from the texture it belongs to
27491
27492 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27493
27494 _cache.invalidated = true; // retire the texture if its utility is low
27495
27496 self.checkTextureUtility(txr);
27497 }
27498 } // remove from queue since the old req was for the old state
27499
27500
27501 self.removeFromQueue(ele);
27502};
27503
27504ETCp.checkTextureUtility = function (txr) {
27505 // invalidate all entries in the cache if the cache size is small
27506 if (txr.invalidatedWidth >= minUtility * txr.width) {
27507 this.retireTexture(txr);
27508 }
27509};
27510
27511ETCp.checkTextureFullness = function (txr) {
27512 // if texture has been mostly filled and passed over several times, remove
27513 // it from the queue so we don't need to waste time looking at it to put new things
27514 var self = this;
27515 var txrQ = self.getTextureQueue(txr.height);
27516
27517 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27518 removeFromArray(txrQ, txr);
27519 } else {
27520 txr.fullnessChecks++;
27521 }
27522};
27523
27524ETCp.retireTexture = function (txr) {
27525 var self = this;
27526 var txrH = txr.height;
27527 var txrQ = self.getTextureQueue(txrH);
27528 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27529
27530 removeFromArray(txrQ, txr);
27531 txr.retired = true; // remove the refs from the eles to the caches:
27532
27533 var eleCaches = txr.eleCaches;
27534
27535 for (var i = 0; i < eleCaches.length; i++) {
27536 var eleCache = eleCaches[i];
27537 lookup.deleteCache(eleCache.key, eleCache.level);
27538 }
27539
27540 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27541
27542 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27543 rtxtrQ.push(txr);
27544};
27545
27546ETCp.addTexture = function (txrH, minW) {
27547 var self = this;
27548 var txrQ = self.getTextureQueue(txrH);
27549 var txr = {};
27550 txrQ.push(txr);
27551 txr.eleCaches = [];
27552 txr.height = txrH;
27553 txr.width = Math.max(defTxrWidth, minW);
27554 txr.usedWidth = 0;
27555 txr.invalidatedWidth = 0;
27556 txr.fullnessChecks = 0;
27557 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27558 txr.context = txr.canvas.getContext('2d');
27559 return txr;
27560};
27561
27562ETCp.recycleTexture = function (txrH, minW) {
27563 var self = this;
27564 var txrQ = self.getTextureQueue(txrH);
27565 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27566
27567 for (var i = 0; i < rtxtrQ.length; i++) {
27568 var txr = rtxtrQ[i];
27569
27570 if (txr.width >= minW) {
27571 txr.retired = false;
27572 txr.usedWidth = 0;
27573 txr.invalidatedWidth = 0;
27574 txr.fullnessChecks = 0;
27575 clearArray(txr.eleCaches);
27576 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27577 txr.context.clearRect(0, 0, txr.width, txr.height);
27578 removeFromArray(rtxtrQ, txr);
27579 txrQ.push(txr);
27580 return txr;
27581 }
27582 }
27583};
27584
27585ETCp.queueElement = function (ele, lvl) {
27586 var self = this;
27587 var q = self.getElementQueue();
27588 var k2q = self.getElementKeyToQueue();
27589 var key = this.getKey(ele);
27590 var existingReq = k2q[key];
27591
27592 if (existingReq) {
27593 // use the max lvl b/c in between lvls are cheap to make
27594 existingReq.level = Math.max(existingReq.level, lvl);
27595 existingReq.eles.merge(ele);
27596 existingReq.reqs++;
27597 q.updateItem(existingReq);
27598 } else {
27599 var req = {
27600 eles: ele.spawn().merge(ele),
27601 level: lvl,
27602 reqs: 1,
27603 key: key
27604 };
27605 q.push(req);
27606 k2q[key] = req;
27607 }
27608};
27609
27610ETCp.dequeue = function (pxRatio
27611/*, extent*/
27612) {
27613 var self = this;
27614 var q = self.getElementQueue();
27615 var k2q = self.getElementKeyToQueue();
27616 var dequeued = [];
27617 var lookup = self.lookup;
27618
27619 for (var i = 0; i < maxDeqSize; i++) {
27620 if (q.size() > 0) {
27621 var req = q.pop();
27622 var key = req.key;
27623 var ele = req.eles[0]; // all eles have the same key
27624
27625 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27626
27627 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27628
27629 if (cacheExists) {
27630 continue;
27631 }
27632
27633 dequeued.push(req);
27634 var bb = self.getBoundingBox(ele);
27635 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27636 } else {
27637 break;
27638 }
27639 }
27640
27641 return dequeued;
27642};
27643
27644ETCp.removeFromQueue = function (ele) {
27645 var self = this;
27646 var q = self.getElementQueue();
27647 var k2q = self.getElementKeyToQueue();
27648 var key = this.getKey(ele);
27649 var req = k2q[key];
27650
27651 if (req != null) {
27652 if (req.eles.length === 1) {
27653 // remove if last ele in the req
27654 // bring to front of queue
27655 req.reqs = MAX_INT;
27656 q.updateItem(req);
27657 q.pop(); // remove from queue
27658
27659 k2q[key] = null; // remove from lookup map
27660 } else {
27661 // otherwise just remove ele from req
27662 req.eles.unmerge(ele);
27663 }
27664 }
27665};
27666
27667ETCp.onDequeue = function (fn) {
27668 this.onDequeues.push(fn);
27669};
27670
27671ETCp.offDequeue = function (fn) {
27672 removeFromArray(this.onDequeues, fn);
27673};
27674
27675ETCp.setupDequeueing = defs.setupDequeueing({
27676 deqRedrawThreshold: deqRedrawThreshold,
27677 deqCost: deqCost,
27678 deqAvgCost: deqAvgCost,
27679 deqNoDrawCost: deqNoDrawCost,
27680 deqFastCost: deqFastCost,
27681 deq: function deq(self, pxRatio, extent) {
27682 return self.dequeue(pxRatio, extent);
27683 },
27684 onDeqd: function onDeqd(self, deqd) {
27685 for (var i = 0; i < self.onDequeues.length; i++) {
27686 var fn = self.onDequeues[i];
27687 fn(deqd);
27688 }
27689 },
27690 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27691 for (var i = 0; i < deqd.length; i++) {
27692 var eles = deqd[i].eles;
27693
27694 for (var j = 0; j < eles.length; j++) {
27695 var bb = eles[j].boundingBox();
27696
27697 if (boundingBoxesIntersect(bb, extent)) {
27698 return true;
27699 }
27700 }
27701 }
27702
27703 return false;
27704 },
27705 priority: function priority(self) {
27706 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27707 }
27708});
27709
27710var defNumLayers = 1; // default number of layers to use
27711
27712var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27713
27714var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
27715
27716var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
27717
27718var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27719
27720var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27721
27722var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27723
27724var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27725
27726var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27727
27728var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27729
27730var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27731
27732var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27733
27734var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27735
27736var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27737// var log = function(){ console.log.apply( console, arguments ); };
27738
27739var LayeredTextureCache = function LayeredTextureCache(renderer) {
27740 var self = this;
27741 var r = self.renderer = renderer;
27742 var cy = r.cy;
27743 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27744
27745 self.firstGet = true;
27746 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27747 self.skipping = false;
27748 self.eleTxrDeqs = cy.collection();
27749 self.scheduleElementRefinement = util(function () {
27750 self.refineElementTextures(self.eleTxrDeqs);
27751 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27752 }, refineEleDebounceTime);
27753 r.beforeRender(function (willDraw, now) {
27754 if (now - self.lastInvalidationTime <= invalidThreshold) {
27755 self.skipping = true;
27756 } else {
27757 self.skipping = false;
27758 }
27759 }, r.beforeRenderPriorities.lyrTxrSkip);
27760
27761 var qSort = function qSort(a, b) {
27762 return b.reqs - a.reqs;
27763 };
27764
27765 self.layersQueue = new Heap(qSort);
27766 self.setupDequeueing();
27767};
27768
27769var LTCp = LayeredTextureCache.prototype;
27770var layerIdPool = 0;
27771var MAX_INT$1 = Math.pow(2, 53) - 1;
27772
27773LTCp.makeLayer = function (bb, lvl) {
27774 var scale = Math.pow(2, lvl);
27775 var w = Math.ceil(bb.w * scale);
27776 var h = Math.ceil(bb.h * scale);
27777 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27778 var layer = {
27779 id: layerIdPool = ++layerIdPool % MAX_INT$1,
27780 bb: bb,
27781 level: lvl,
27782 width: w,
27783 height: h,
27784 canvas: canvas,
27785 context: canvas.getContext('2d'),
27786 eles: [],
27787 elesQueue: [],
27788 reqs: 0
27789 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27790
27791 var cxt = layer.context;
27792 var dx = -layer.bb.x1;
27793 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
27794
27795 cxt.scale(scale, scale);
27796 cxt.translate(dx, dy);
27797 return layer;
27798};
27799
27800LTCp.getLayers = function (eles, pxRatio, lvl) {
27801 var self = this;
27802 var r = self.renderer;
27803 var cy = r.cy;
27804 var zoom = cy.zoom();
27805 var firstGet = self.firstGet;
27806 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
27807 //log eles.map(function(ele){ return ele.id() }) );
27808
27809 if (lvl == null) {
27810 lvl = Math.ceil(log2(zoom * pxRatio));
27811
27812 if (lvl < minLvl$1) {
27813 lvl = minLvl$1;
27814 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27815 return null;
27816 }
27817 }
27818
27819 self.validateLayersElesOrdering(lvl, eles);
27820 var layersByLvl = self.layersByLevel;
27821 var scale = Math.pow(2, lvl);
27822 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
27823 var bb;
27824 var lvlComplete = self.levelIsComplete(lvl, eles);
27825 var tmpLayers;
27826
27827 var checkTempLevels = function checkTempLevels() {
27828 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
27829 self.validateLayersElesOrdering(l, eles);
27830
27831 if (self.levelIsComplete(l, eles)) {
27832 tmpLayers = layersByLvl[l];
27833 return true;
27834 }
27835 };
27836
27837 var checkLvls = function checkLvls(dir) {
27838 if (tmpLayers) {
27839 return;
27840 }
27841
27842 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
27843 if (canUseAsTmpLvl(l)) {
27844 break;
27845 }
27846 }
27847 };
27848
27849 checkLvls(+1);
27850 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
27851
27852 for (var i = layers.length - 1; i >= 0; i--) {
27853 var layer = layers[i];
27854
27855 if (layer.invalid) {
27856 removeFromArray(layers, layer);
27857 }
27858 }
27859 };
27860
27861 if (!lvlComplete) {
27862 // if the current level is incomplete, then use the closest, best quality layerset temporarily
27863 // and later queue the current layerset so we can get the proper quality level soon
27864 checkTempLevels();
27865 } else {
27866 // log('level complete, using existing layers\n--');
27867 return layers;
27868 }
27869
27870 var getBb = function getBb() {
27871 if (!bb) {
27872 bb = makeBoundingBox();
27873
27874 for (var i = 0; i < eles.length; i++) {
27875 updateBoundingBox(bb, eles[i].boundingBox());
27876 }
27877 }
27878
27879 return bb;
27880 };
27881
27882 var makeLayer = function makeLayer(opts) {
27883 opts = opts || {};
27884 var after = opts.after;
27885 getBb();
27886 var area = bb.w * scale * (bb.h * scale);
27887
27888 if (area > maxLayerArea) {
27889 return null;
27890 }
27891
27892 var layer = self.makeLayer(bb, lvl);
27893
27894 if (after != null) {
27895 var index = layers.indexOf(after) + 1;
27896 layers.splice(index, 0, layer);
27897 } else if (opts.insert === undefined || opts.insert) {
27898 // no after specified => first layer made so put at start
27899 layers.unshift(layer);
27900 } // if( tmpLayers ){
27901 //self.queueLayer( layer );
27902 // }
27903
27904
27905 return layer;
27906 };
27907
27908 if (self.skipping && !firstGet) {
27909 // log('skip layers');
27910 return null;
27911 } // log('do layers');
27912
27913
27914 var layer = null;
27915 var maxElesPerLayer = eles.length / defNumLayers;
27916 var allowLazyQueueing = !firstGet;
27917
27918 for (var i = 0; i < eles.length; i++) {
27919 var ele = eles[i];
27920 var rs = ele._private.rscratch;
27921 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
27922
27923 var existingLayer = caches[lvl];
27924
27925 if (existingLayer) {
27926 // reuse layer for later eles
27927 // log('reuse layer for', ele.id());
27928 layer = existingLayer;
27929 continue;
27930 }
27931
27932 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
27933 // log('make new layer for ele %s', ele.id());
27934 layer = makeLayer({
27935 insert: true,
27936 after: layer
27937 }); // if now layer can be built then we can't use layers at this level
27938
27939 if (!layer) {
27940 return null;
27941 } // log('new layer with id %s', layer.id);
27942
27943 }
27944
27945 if (tmpLayers || allowLazyQueueing) {
27946 // log('queue ele %s in layer %s', ele.id(), layer.id);
27947 self.queueLayer(layer, ele);
27948 } else {
27949 // log('draw ele %s in layer %s', ele.id(), layer.id);
27950 self.drawEleInLayer(layer, ele, lvl, pxRatio);
27951 }
27952
27953 layer.eles.push(ele);
27954 caches[lvl] = layer;
27955 } // log('--');
27956
27957
27958 if (tmpLayers) {
27959 // then we only queued the current layerset and can't draw it yet
27960 return tmpLayers;
27961 }
27962
27963 if (allowLazyQueueing) {
27964 // log('lazy queue level', lvl);
27965 return null;
27966 }
27967
27968 return layers;
27969}; // a layer may want to use an ele cache of a higher level to avoid blurriness
27970// so the layer level might not equal the ele level
27971
27972
27973LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
27974 return lvl;
27975};
27976
27977LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
27978 var self = this;
27979 var r = this.renderer;
27980 var context = layer.context;
27981 var bb = ele.boundingBox();
27982
27983 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
27984 return;
27985 }
27986
27987 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
27988
27989 {
27990 r.setImgSmoothing(context, false);
27991 }
27992
27993 {
27994 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
27995 }
27996
27997 {
27998 r.setImgSmoothing(context, true);
27999 }
28000};
28001
28002LTCp.levelIsComplete = function (lvl, eles) {
28003 var self = this;
28004 var layers = self.layersByLevel[lvl];
28005
28006 if (!layers || layers.length === 0) {
28007 return false;
28008 }
28009
28010 var numElesInLayers = 0;
28011
28012 for (var i = 0; i < layers.length; i++) {
28013 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28014
28015 if (layer.reqs > 0) {
28016 return false;
28017 } // if the layer is invalid, the level is not complete
28018
28019
28020 if (layer.invalid) {
28021 return false;
28022 }
28023
28024 numElesInLayers += layer.eles.length;
28025 } // we should have exactly the number of eles passed in to be complete
28026
28027
28028 if (numElesInLayers !== eles.length) {
28029 return false;
28030 }
28031
28032 return true;
28033};
28034
28035LTCp.validateLayersElesOrdering = function (lvl, eles) {
28036 var layers = this.layersByLevel[lvl];
28037
28038 if (!layers) {
28039 return;
28040 } // if in a layer the eles are not in the same order, then the layer is invalid
28041 // (i.e. there is an ele in between the eles in the layer)
28042
28043
28044 for (var i = 0; i < layers.length; i++) {
28045 var layer = layers[i];
28046 var offset = -1; // find the offset
28047
28048 for (var j = 0; j < eles.length; j++) {
28049 if (layer.eles[0] === eles[j]) {
28050 offset = j;
28051 break;
28052 }
28053 }
28054
28055 if (offset < 0) {
28056 // then the layer has nonexistant elements and is invalid
28057 this.invalidateLayer(layer);
28058 continue;
28059 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28060
28061
28062 var o = offset;
28063
28064 for (var j = 0; j < layer.eles.length; j++) {
28065 if (layer.eles[j] !== eles[o + j]) {
28066 // log('invalidate based on ordering', layer.id);
28067 this.invalidateLayer(layer);
28068 break;
28069 }
28070 }
28071 }
28072};
28073
28074LTCp.updateElementsInLayers = function (eles, update) {
28075 var self = this;
28076 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28077 // layer itself along the way
28078
28079 for (var i = 0; i < eles.length; i++) {
28080 var req = isEles ? null : eles[i];
28081 var ele = isEles ? eles[i] : eles[i].ele;
28082 var rs = ele._private.rscratch;
28083 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28084
28085 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28086 var layer = caches[l];
28087
28088 if (!layer) {
28089 continue;
28090 } // if update is a request from the ele cache, then it affects only
28091 // the matching level
28092
28093
28094 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28095 continue;
28096 }
28097
28098 update(layer, ele, req);
28099 }
28100 }
28101};
28102
28103LTCp.haveLayers = function () {
28104 var self = this;
28105 var haveLayers = false;
28106
28107 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28108 var layers = self.layersByLevel[l];
28109
28110 if (layers && layers.length > 0) {
28111 haveLayers = true;
28112 break;
28113 }
28114 }
28115
28116 return haveLayers;
28117};
28118
28119LTCp.invalidateElements = function (eles) {
28120 var self = this;
28121
28122 if (eles.length === 0) {
28123 return;
28124 }
28125
28126 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28127
28128 if (eles.length === 0 || !self.haveLayers()) {
28129 return;
28130 }
28131
28132 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28133 self.invalidateLayer(layer);
28134 });
28135};
28136
28137LTCp.invalidateLayer = function (layer) {
28138 // log('update invalidate layer time');
28139 this.lastInvalidationTime = performanceNow();
28140
28141 if (layer.invalid) {
28142 return;
28143 } // save cycles
28144
28145
28146 var lvl = layer.level;
28147 var eles = layer.eles;
28148 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28149
28150 removeFromArray(layers, layer); // layer.eles = [];
28151
28152 layer.elesQueue = [];
28153 layer.invalid = true;
28154
28155 if (layer.replacement) {
28156 layer.replacement.invalid = true;
28157 }
28158
28159 for (var i = 0; i < eles.length; i++) {
28160 var caches = eles[i]._private.rscratch.imgLayerCaches;
28161
28162 if (caches) {
28163 caches[lvl] = null;
28164 }
28165 }
28166};
28167
28168LTCp.refineElementTextures = function (eles) {
28169 var self = this; // log('refine', eles.length);
28170
28171 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28172 var rLyr = layer.replacement;
28173
28174 if (!rLyr) {
28175 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28176 rLyr.replaces = layer;
28177 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28178 }
28179
28180 if (!rLyr.reqs) {
28181 for (var i = 0; i < rLyr.eles.length; i++) {
28182 self.queueLayer(rLyr, rLyr.eles[i]);
28183 } // log('queue replacement layer refinement', rLyr.id);
28184
28185 }
28186 });
28187};
28188
28189LTCp.enqueueElementRefinement = function (ele) {
28190
28191 this.eleTxrDeqs.merge(ele);
28192 this.scheduleElementRefinement();
28193};
28194
28195LTCp.queueLayer = function (layer, ele) {
28196 var self = this;
28197 var q = self.layersQueue;
28198 var elesQ = layer.elesQueue;
28199 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28200
28201 if (layer.replacement) {
28202 return;
28203 }
28204
28205 if (ele) {
28206 if (hasId[ele.id()]) {
28207 return;
28208 }
28209
28210 elesQ.push(ele);
28211 hasId[ele.id()] = true;
28212 }
28213
28214 if (layer.reqs) {
28215 layer.reqs++;
28216 q.updateItem(layer);
28217 } else {
28218 layer.reqs = 1;
28219 q.push(layer);
28220 }
28221};
28222
28223LTCp.dequeue = function (pxRatio) {
28224 var self = this;
28225 var q = self.layersQueue;
28226 var deqd = [];
28227 var eleDeqs = 0;
28228
28229 while (eleDeqs < maxDeqSize$1) {
28230 if (q.size() === 0) {
28231 break;
28232 }
28233
28234 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28235
28236 if (layer.replacement) {
28237 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28238 q.pop();
28239 continue;
28240 } // if this is a replacement layer that has been superceded, then forget it
28241
28242
28243 if (layer.replaces && layer !== layer.replaces.replacement) {
28244 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28245 q.pop();
28246 continue;
28247 }
28248
28249 if (layer.invalid) {
28250 // log('replacement layer %s is invalid; dequeued', layer.id);
28251 q.pop();
28252 continue;
28253 }
28254
28255 var ele = layer.elesQueue.shift();
28256
28257 if (ele) {
28258 // log('dequeue layer %s', layer.id);
28259 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28260 eleDeqs++;
28261 }
28262
28263 if (deqd.length === 0) {
28264 // we need only one entry in deqd to queue redrawing etc
28265 deqd.push(true);
28266 } // if the layer has all its eles done, then remove from the queue
28267
28268
28269 if (layer.elesQueue.length === 0) {
28270 q.pop();
28271 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28272 // when a replacement layer is dequeued, it replaces the old layer in the level
28273
28274 if (layer.replaces) {
28275 self.applyLayerReplacement(layer);
28276 }
28277
28278 self.requestRedraw();
28279 }
28280 }
28281
28282 return deqd;
28283};
28284
28285LTCp.applyLayerReplacement = function (layer) {
28286 var self = this;
28287 var layersInLevel = self.layersByLevel[layer.level];
28288 var replaced = layer.replaces;
28289 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28290 // refs would be a mistake (i.e. overwriting the true active layer)
28291
28292 if (index < 0 || replaced.invalid) {
28293 // log('replacement layer would have no effect', layer.id);
28294 return;
28295 }
28296
28297 layersInLevel[index] = layer; // replace level ref
28298 // replace refs in eles
28299
28300 for (var i = 0; i < layer.eles.length; i++) {
28301 var _p = layer.eles[i]._private;
28302 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28303
28304 if (cache) {
28305 cache[layer.level] = layer;
28306 }
28307 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
28308
28309
28310 self.requestRedraw();
28311};
28312
28313LTCp.requestRedraw = util(function () {
28314 var r = this.renderer;
28315 r.redrawHint('eles', true);
28316 r.redrawHint('drag', true);
28317 r.redraw();
28318}, 100);
28319LTCp.setupDequeueing = defs.setupDequeueing({
28320 deqRedrawThreshold: deqRedrawThreshold$1,
28321 deqCost: deqCost$1,
28322 deqAvgCost: deqAvgCost$1,
28323 deqNoDrawCost: deqNoDrawCost$1,
28324 deqFastCost: deqFastCost$1,
28325 deq: function deq(self, pxRatio) {
28326 return self.dequeue(pxRatio);
28327 },
28328 onDeqd: noop,
28329 shouldRedraw: trueify,
28330 priority: function priority(self) {
28331 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28332 }
28333});
28334
28335var CRp = {};
28336var impl;
28337
28338function polygon(context, points) {
28339 for (var i = 0; i < points.length; i++) {
28340 var pt = points[i];
28341 context.lineTo(pt.x, pt.y);
28342 }
28343}
28344
28345function triangleBackcurve(context, points, controlPoint) {
28346 var firstPt;
28347
28348 for (var i = 0; i < points.length; i++) {
28349 var pt = points[i];
28350
28351 if (i === 0) {
28352 firstPt = pt;
28353 }
28354
28355 context.lineTo(pt.x, pt.y);
28356 }
28357
28358 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28359}
28360
28361function triangleTee(context, trianglePoints, teePoints) {
28362 if (context.beginPath) {
28363 context.beginPath();
28364 }
28365
28366 var triPts = trianglePoints;
28367
28368 for (var i = 0; i < triPts.length; i++) {
28369 var pt = triPts[i];
28370 context.lineTo(pt.x, pt.y);
28371 }
28372
28373 var teePts = teePoints;
28374 var firstTeePt = teePoints[0];
28375 context.moveTo(firstTeePt.x, firstTeePt.y);
28376
28377 for (var i = 1; i < teePts.length; i++) {
28378 var pt = teePts[i];
28379 context.lineTo(pt.x, pt.y);
28380 }
28381
28382 if (context.closePath) {
28383 context.closePath();
28384 }
28385}
28386
28387function circleTriangle(context, trianglePoints, rx, ry, r) {
28388 if (context.beginPath) {
28389 context.beginPath();
28390 }
28391
28392 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28393 var triPts = trianglePoints;
28394 var firstTrPt = triPts[0];
28395 context.moveTo(firstTrPt.x, firstTrPt.y);
28396
28397 for (var i = 0; i < triPts.length; i++) {
28398 var pt = triPts[i];
28399 context.lineTo(pt.x, pt.y);
28400 }
28401
28402 if (context.closePath) {
28403 context.closePath();
28404 }
28405}
28406
28407function circle(context, rx, ry, r) {
28408 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28409}
28410
28411CRp.arrowShapeImpl = function (name) {
28412 return (impl || (impl = {
28413 'polygon': polygon,
28414 'triangle-backcurve': triangleBackcurve,
28415 'triangle-tee': triangleTee,
28416 'circle-triangle': circleTriangle,
28417 'triangle-cross': triangleTee,
28418 'circle': circle
28419 }))[name];
28420};
28421
28422var CRp$1 = {};
28423
28424CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28425 var r = this;
28426
28427 if (ele.isNode()) {
28428 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28429 } else {
28430 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28431 }
28432};
28433
28434CRp$1.drawElementOverlay = function (context, ele) {
28435 var r = this;
28436
28437 if (ele.isNode()) {
28438 r.drawNodeOverlay(context, ele);
28439 } else {
28440 r.drawEdgeOverlay(context, ele);
28441 }
28442};
28443
28444CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28445 var r = this;
28446 var bb = eleTxrCache.getBoundingBox(ele);
28447
28448 if (bb.w === 0 || bb.h === 0) {
28449 return;
28450 } // ignore zero size case
28451
28452
28453 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28454
28455 if (eleCache != null) {
28456 var opacity = getOpacity(r, ele);
28457
28458 if (opacity === 0) {
28459 return;
28460 }
28461
28462 var theta = getRotation(r, ele);
28463 var x1 = bb.x1,
28464 y1 = bb.y1,
28465 w = bb.w,
28466 h = bb.h;
28467 var x, y, sx, sy, smooth;
28468
28469 if (theta !== 0) {
28470 var rotPt = eleTxrCache.getRotationPoint(ele);
28471 sx = rotPt.x;
28472 sy = rotPt.y;
28473 context.translate(sx, sy);
28474 context.rotate(theta);
28475 smooth = r.getImgSmoothing(context);
28476
28477 if (!smooth) {
28478 r.setImgSmoothing(context, true);
28479 }
28480
28481 var off = eleTxrCache.getRotationOffset(ele);
28482 x = off.x;
28483 y = off.y;
28484 } else {
28485 x = x1;
28486 y = y1;
28487 }
28488
28489 var oldGlobalAlpha;
28490
28491 if (opacity !== 1) {
28492 oldGlobalAlpha = context.globalAlpha;
28493 context.globalAlpha = oldGlobalAlpha * opacity;
28494 }
28495
28496 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28497
28498 if (opacity !== 1) {
28499 context.globalAlpha = oldGlobalAlpha;
28500 }
28501
28502 if (theta !== 0) {
28503 context.rotate(-theta);
28504 context.translate(-sx, -sy);
28505
28506 if (!smooth) {
28507 r.setImgSmoothing(context, false);
28508 }
28509 }
28510 } else {
28511 eleTxrCache.drawElement(context, ele); // direct draw fallback
28512 }
28513};
28514
28515var getZeroRotation = function getZeroRotation() {
28516 return 0;
28517};
28518
28519var getLabelRotation = function getLabelRotation(r, ele) {
28520 return r.getTextAngle(ele, null);
28521};
28522
28523var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28524 return r.getTextAngle(ele, 'source');
28525};
28526
28527var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28528 return r.getTextAngle(ele, 'target');
28529};
28530
28531var getOpacity = function getOpacity(r, ele) {
28532 return ele.effectiveOpacity();
28533};
28534
28535var getTextOpacity = function getTextOpacity(e, ele) {
28536 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28537};
28538
28539CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28540 var r = this;
28541 var _r$data = r.data,
28542 eleTxrCache = _r$data.eleTxrCache,
28543 lblTxrCache = _r$data.lblTxrCache,
28544 slbTxrCache = _r$data.slbTxrCache,
28545 tlbTxrCache = _r$data.tlbTxrCache;
28546 var bb = ele.boundingBox();
28547 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28548
28549 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28550 return;
28551 }
28552
28553 if (!extent || boundingBoxesIntersect(bb, extent)) {
28554 var isEdge = ele.isEdge();
28555
28556 var badLine = ele.element()._private.rscratch.badLine;
28557
28558 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28559
28560 if (!isEdge || !badLine) {
28561 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28562 }
28563
28564 if (isEdge && !badLine) {
28565 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28566 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28567 }
28568
28569 r.drawElementOverlay(context, ele);
28570 }
28571};
28572
28573CRp$1.drawElements = function (context, eles) {
28574 var r = this;
28575
28576 for (var i = 0; i < eles.length; i++) {
28577 var ele = eles[i];
28578 r.drawElement(context, ele);
28579 }
28580};
28581
28582CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
28583 var r = this;
28584
28585 for (var i = 0; i < eles.length; i++) {
28586 var ele = eles[i];
28587 r.drawCachedElement(context, ele, pxRatio, extent);
28588 }
28589};
28590
28591CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
28592 var r = this;
28593
28594 for (var i = 0; i < eles.length; i++) {
28595 var ele = eles[i];
28596
28597 if (!ele.isNode()) {
28598 continue;
28599 }
28600
28601 r.drawCachedElement(context, ele, pxRatio, extent);
28602 }
28603};
28604
28605CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
28606 var r = this;
28607 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28608
28609 if (layers) {
28610 for (var i = 0; i < layers.length; i++) {
28611 var layer = layers[i];
28612 var bb = layer.bb;
28613
28614 if (bb.w === 0 || bb.h === 0) {
28615 continue;
28616 }
28617
28618 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28619 }
28620 } else {
28621 // fall back on plain caching if no layers
28622 r.drawCachedElements(context, eles, pxRatio, extent);
28623 }
28624};
28625
28626/* global Path2D */
28627var CRp$2 = {};
28628
28629CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
28630 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28631 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28632 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28633 var r = this;
28634 var rs = edge._private.rscratch;
28635
28636 if (shouldDrawOpacity && !edge.visible()) {
28637 return;
28638 } // if bezier ctrl pts can not be calculated, then die
28639
28640
28641 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28642 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28643 return;
28644 }
28645
28646 var bb;
28647
28648 if (shiftToOriginWithBb) {
28649 bb = shiftToOriginWithBb;
28650 context.translate(-bb.x1, -bb.y1);
28651 }
28652
28653 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28654 var lineStyle = edge.pstyle('line-style').value;
28655 var edgeWidth = edge.pstyle('width').pfValue;
28656 var lineCap = edge.pstyle('line-cap').value;
28657
28658 var drawLine = function drawLine() {
28659 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
28660 context.lineWidth = edgeWidth;
28661 context.lineCap = lineCap;
28662 r.eleStrokeStyle(context, edge, strokeOpacity);
28663 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28664 context.lineCap = 'butt'; // reset for other drawing functions
28665 };
28666
28667 var drawOverlay = function drawOverlay() {
28668 if (!shouldDrawOverlay) {
28669 return;
28670 }
28671
28672 r.drawEdgeOverlay(context, edge);
28673 };
28674
28675 var drawArrows = function drawArrows() {
28676 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
28677 r.drawArrowheads(context, edge, arrowOpacity);
28678 };
28679
28680 var drawText = function drawText() {
28681 r.drawElementText(context, edge, null, drawLabel);
28682 };
28683
28684 context.lineJoin = 'round';
28685 var ghost = edge.pstyle('ghost').value === 'yes';
28686
28687 if (ghost) {
28688 var gx = edge.pstyle('ghost-offset-x').pfValue;
28689 var gy = edge.pstyle('ghost-offset-y').pfValue;
28690 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28691 var effectiveGhostOpacity = opacity * ghostOpacity;
28692 context.translate(gx, gy);
28693 drawLine(effectiveGhostOpacity);
28694 drawArrows(effectiveGhostOpacity);
28695 context.translate(-gx, -gy);
28696 }
28697
28698 drawLine();
28699 drawArrows();
28700 drawOverlay();
28701 drawText();
28702
28703 if (shiftToOriginWithBb) {
28704 context.translate(bb.x1, bb.y1);
28705 }
28706};
28707
28708CRp$2.drawEdgeOverlay = function (context, edge) {
28709 if (!edge.visible()) {
28710 return;
28711 }
28712
28713 var overlayOpacity = edge.pstyle('overlay-opacity').value;
28714
28715 if (overlayOpacity === 0) {
28716 return;
28717 }
28718
28719 var r = this;
28720 var usePaths = r.usePaths();
28721 var rs = edge._private.rscratch;
28722 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
28723 var overlayWidth = 2 * overlayPadding;
28724 var overlayColor = edge.pstyle('overlay-color').value;
28725 context.lineWidth = overlayWidth;
28726
28727 if (rs.edgeType === 'self' && !usePaths) {
28728 context.lineCap = 'butt';
28729 } else {
28730 context.lineCap = 'round';
28731 }
28732
28733 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
28734 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28735};
28736
28737CRp$2.drawEdgePath = function (edge, context, pts, type) {
28738 var rs = edge._private.rscratch;
28739 var canvasCxt = context;
28740 var path;
28741 var pathCacheHit = false;
28742 var usePaths = this.usePaths();
28743 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28744 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28745
28746 if (usePaths) {
28747 var pathCacheKey = pts.join('$');
28748 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28749
28750 if (keyMatches) {
28751 path = context = rs.pathCache;
28752 pathCacheHit = true;
28753 } else {
28754 path = context = new Path2D();
28755 rs.pathCacheKey = pathCacheKey;
28756 rs.pathCache = path;
28757 }
28758 }
28759
28760 if (canvasCxt.setLineDash) {
28761 // for very outofdate browsers
28762 switch (type) {
28763 case 'dotted':
28764 canvasCxt.setLineDash([1, 1]);
28765 break;
28766
28767 case 'dashed':
28768 canvasCxt.setLineDash(lineDashPattern);
28769 canvasCxt.lineDashOffset = lineDashOffset;
28770 break;
28771
28772 case 'solid':
28773 canvasCxt.setLineDash([]);
28774 break;
28775 }
28776 }
28777
28778 if (!pathCacheHit && !rs.badLine) {
28779 if (context.beginPath) {
28780 context.beginPath();
28781 }
28782
28783 context.moveTo(pts[0], pts[1]);
28784
28785 switch (rs.edgeType) {
28786 case 'bezier':
28787 case 'self':
28788 case 'compound':
28789 case 'multibezier':
28790 for (var i = 2; i + 3 < pts.length; i += 4) {
28791 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
28792 }
28793
28794 break;
28795
28796 case 'straight':
28797 case 'segments':
28798 case 'haystack':
28799 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
28800 context.lineTo(pts[_i], pts[_i + 1]);
28801 }
28802
28803 break;
28804 }
28805 }
28806
28807 context = canvasCxt;
28808
28809 if (usePaths) {
28810 context.stroke(path);
28811 } else {
28812 context.stroke();
28813 } // reset any line dashes
28814
28815
28816 if (context.setLineDash) {
28817 // for very outofdate browsers
28818 context.setLineDash([]);
28819 }
28820};
28821
28822CRp$2.drawArrowheads = function (context, edge, opacity) {
28823 var rs = edge._private.rscratch;
28824 var isHaystack = rs.edgeType === 'haystack';
28825
28826 if (!isHaystack) {
28827 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
28828 }
28829
28830 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
28831 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
28832
28833 if (!isHaystack) {
28834 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
28835 }
28836};
28837
28838CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
28839 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
28840 return;
28841 }
28842
28843 var self = this;
28844 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
28845
28846 if (arrowShape === 'none') {
28847 return;
28848 }
28849
28850 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
28851 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
28852 var edgeWidth = edge.pstyle('width').pfValue;
28853 var edgeOpacity = edge.pstyle('opacity').value;
28854
28855 if (opacity === undefined) {
28856 opacity = edgeOpacity;
28857 }
28858
28859 var gco = context.globalCompositeOperation;
28860
28861 if (opacity !== 1 || arrowFill === 'hollow') {
28862 // then extra clear is needed
28863 context.globalCompositeOperation = 'destination-out';
28864 self.colorFillStyle(context, 255, 255, 255, 1);
28865 self.colorStrokeStyle(context, 255, 255, 255, 1);
28866 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
28867 context.globalCompositeOperation = gco;
28868 } // otherwise, the opaque arrow clears it for free :)
28869
28870
28871 var color = edge.pstyle(prefix + '-arrow-color').value;
28872 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
28873 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28874 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
28875};
28876
28877CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
28878 var r = this;
28879 var usePaths = this.usePaths() && shape !== 'triangle-cross';
28880 var pathCacheHit = false;
28881 var path;
28882 var canvasContext = context;
28883 var translation = {
28884 x: x,
28885 y: y
28886 };
28887 var scale = edge.pstyle('arrow-scale').value;
28888 var size = this.getArrowWidth(edgeWidth, scale);
28889 var shapeImpl = r.arrowShapes[shape];
28890
28891 if (usePaths) {
28892 var cache = r.arrowPathCache = r.arrowPathCache || [];
28893 var key = hashString(shape);
28894 var cachedPath = cache[key];
28895
28896 if (cachedPath != null) {
28897 path = context = cachedPath;
28898 pathCacheHit = true;
28899 } else {
28900 path = context = new Path2D();
28901 cache[key] = path;
28902 }
28903 }
28904
28905 if (!pathCacheHit) {
28906 if (context.beginPath) {
28907 context.beginPath();
28908 }
28909
28910 if (usePaths) {
28911 // store in the path cache with values easily manipulated later
28912 shapeImpl.draw(context, 1, 0, {
28913 x: 0,
28914 y: 0
28915 }, 1);
28916 } else {
28917 shapeImpl.draw(context, size, angle, translation, edgeWidth);
28918 }
28919
28920 if (context.closePath) {
28921 context.closePath();
28922 }
28923 }
28924
28925 context = canvasContext;
28926
28927 if (usePaths) {
28928 // set transform to arrow position/orientation
28929 context.translate(x, y);
28930 context.rotate(angle);
28931 context.scale(size, size);
28932 }
28933
28934 if (fill === 'filled' || fill === 'both') {
28935 if (usePaths) {
28936 context.fill(path);
28937 } else {
28938 context.fill();
28939 }
28940 }
28941
28942 if (fill === 'hollow' || fill === 'both') {
28943 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
28944 context.lineJoin = 'miter';
28945
28946 if (usePaths) {
28947 context.stroke(path);
28948 } else {
28949 context.stroke();
28950 }
28951 }
28952
28953 if (usePaths) {
28954 // reset transform by applying inverse
28955 context.scale(1 / size, 1 / size);
28956 context.rotate(-angle);
28957 context.translate(-x, -y);
28958 }
28959};
28960
28961var CRp$3 = {};
28962
28963CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
28964 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
28965 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
28966 return;
28967 }
28968
28969 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
28970};
28971
28972CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
28973 var r = this;
28974 var pos = node.position();
28975 var nodeX = pos.x;
28976 var nodeY = pos.y;
28977 var styleObj = node.cy().style();
28978 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
28979 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
28980 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
28981 var nodeW = node.width();
28982 var nodeH = node.height();
28983 var paddingX2 = node.padding() * 2;
28984 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28985 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28986 var rs = node._private.rscratch;
28987 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
28988 var shouldClip = clip === 'node';
28989 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
28990 var imgW = img.width || img.cachedW;
28991 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
28992
28993 if (null == imgW || null == imgH) {
28994 document.body.appendChild(img); // eslint-disable-line no-undef
28995
28996 imgW = img.cachedW = img.width || img.offsetWidth;
28997 imgH = img.cachedH = img.height || img.offsetHeight;
28998 document.body.removeChild(img); // eslint-disable-line no-undef
28999 }
29000
29001 var w = imgW;
29002 var h = imgH;
29003
29004 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29005 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29006 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29007 } else {
29008 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29009 }
29010 }
29011
29012 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29013 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29014 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29015 } else {
29016 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29017 }
29018 }
29019
29020 if (w === 0 || h === 0) {
29021 return; // no point in drawing empty image (and chrome is broken in this case)
29022 }
29023
29024 if (fit === 'contain') {
29025 var scale = Math.min(nodeTW / w, nodeTH / h);
29026 w *= scale;
29027 h *= scale;
29028 } else if (fit === 'cover') {
29029 var scale = Math.max(nodeTW / w, nodeTH / h);
29030 w *= scale;
29031 h *= scale;
29032 }
29033
29034 var x = nodeX - nodeTW / 2; // left
29035
29036 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29037 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29038
29039 if (posXUnits === '%') {
29040 x += (nodeTW - w) * posXPfVal;
29041 } else {
29042 x += posXPfVal;
29043 }
29044
29045 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29046 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29047
29048 if (offXUnits === '%') {
29049 x += (nodeTW - w) * offXPfVal;
29050 } else {
29051 x += offXPfVal;
29052 }
29053
29054 var y = nodeY - nodeTH / 2; // top
29055
29056 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29057 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29058
29059 if (posYUnits === '%') {
29060 y += (nodeTH - h) * posYPfVal;
29061 } else {
29062 y += posYPfVal;
29063 }
29064
29065 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29066 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29067
29068 if (offYUnits === '%') {
29069 y += (nodeTH - h) * offYPfVal;
29070 } else {
29071 y += offYPfVal;
29072 }
29073
29074 if (rs.pathCache) {
29075 x -= nodeX;
29076 y -= nodeY;
29077 nodeX = 0;
29078 nodeY = 0;
29079 }
29080
29081 var gAlpha = context.globalAlpha;
29082 context.globalAlpha = imgOpacity;
29083
29084 if (repeat === 'no-repeat') {
29085 if (shouldClip) {
29086 context.save();
29087
29088 if (rs.pathCache) {
29089 context.clip(rs.pathCache);
29090 } else {
29091 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29092 context.clip();
29093 }
29094 }
29095
29096 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29097
29098 if (shouldClip) {
29099 context.restore();
29100 }
29101 } else {
29102 var pattern = context.createPattern(img, repeat);
29103 context.fillStyle = pattern;
29104 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29105 context.translate(x, y);
29106 context.fill();
29107 context.translate(-x, -y);
29108 }
29109
29110 context.globalAlpha = gAlpha;
29111};
29112
29113var CRp$4 = {};
29114
29115CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29116 if (!scale) {
29117 var zoom = ele.cy().zoom();
29118 var pxRatio = this.getPixelRatio();
29119 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29120
29121 scale = Math.pow(2, lvl);
29122 }
29123
29124 var computedSize = ele.pstyle('font-size').pfValue * scale;
29125 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29126
29127 if (computedSize < minSize) {
29128 return false;
29129 }
29130
29131 return true;
29132};
29133
29134CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29135 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29136 var r = this;
29137
29138 if (force == null) {
29139 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29140 return;
29141 }
29142 } else if (force === false) {
29143 return;
29144 }
29145
29146 if (ele.isNode()) {
29147 var label = ele.pstyle('label');
29148
29149 if (!label || !label.value) {
29150 return;
29151 }
29152
29153 var justification = r.getLabelJustification(ele);
29154 context.textAlign = justification;
29155 context.textBaseline = 'bottom';
29156 } else {
29157 var badLine = ele.element()._private.rscratch.badLine;
29158
29159 var _label = ele.pstyle('label');
29160
29161 var srcLabel = ele.pstyle('source-label');
29162 var tgtLabel = ele.pstyle('target-label');
29163
29164 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29165 return;
29166 }
29167
29168 context.textAlign = 'center';
29169 context.textBaseline = 'bottom';
29170 }
29171
29172 var applyRotation = !shiftToOriginWithBb;
29173 var bb;
29174
29175 if (shiftToOriginWithBb) {
29176 bb = shiftToOriginWithBb;
29177 context.translate(-bb.x1, -bb.y1);
29178 }
29179
29180 if (prefix == null) {
29181 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29182
29183 if (ele.isEdge()) {
29184 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29185 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29186 }
29187 } else {
29188 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29189 }
29190
29191 if (shiftToOriginWithBb) {
29192 context.translate(bb.x1, bb.y1);
29193 }
29194};
29195
29196CRp$4.getFontCache = function (context) {
29197 var cache;
29198 this.fontCaches = this.fontCaches || [];
29199
29200 for (var i = 0; i < this.fontCaches.length; i++) {
29201 cache = this.fontCaches[i];
29202
29203 if (cache.context === context) {
29204 return cache;
29205 }
29206 }
29207
29208 cache = {
29209 context: context
29210 };
29211 this.fontCaches.push(cache);
29212 return cache;
29213}; // set up canvas context with font
29214// returns transformed text string
29215
29216
29217CRp$4.setupTextStyle = function (context, ele) {
29218 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29219 // Font style
29220 var labelStyle = ele.pstyle('font-style').strValue;
29221 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29222 var labelFamily = ele.pstyle('font-family').strValue;
29223 var labelWeight = ele.pstyle('font-weight').strValue;
29224 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29225 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29226 var color = ele.pstyle('color').value;
29227 var outlineColor = ele.pstyle('text-outline-color').value;
29228 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29229 context.lineJoin = 'round'; // so text outlines aren't jagged
29230
29231 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29232 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29233}; // TODO ensure re-used
29234
29235
29236function roundRect(ctx, x, y, width, height) {
29237 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29238 ctx.beginPath();
29239 ctx.moveTo(x + radius, y);
29240 ctx.lineTo(x + width - radius, y);
29241 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29242 ctx.lineTo(x + width, y + height - radius);
29243 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29244 ctx.lineTo(x + radius, y + height);
29245 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29246 ctx.lineTo(x, y + radius);
29247 ctx.quadraticCurveTo(x, y, x + radius, y);
29248 ctx.closePath();
29249 ctx.fill();
29250}
29251
29252CRp$4.getTextAngle = function (ele, prefix) {
29253 var theta;
29254 var _p = ele._private;
29255 var rscratch = _p.rscratch;
29256 var pdash = prefix ? prefix + '-' : '';
29257 var rotation = ele.pstyle(pdash + 'text-rotation');
29258 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29259
29260 if (rotation.strValue === 'autorotate') {
29261 theta = ele.isEdge() ? textAngle : 0;
29262 } else if (rotation.strValue === 'none') {
29263 theta = 0;
29264 } else {
29265 theta = rotation.pfValue;
29266 }
29267
29268 return theta;
29269};
29270
29271CRp$4.drawText = function (context, ele, prefix) {
29272 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29273 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29274 var _p = ele._private;
29275 var rscratch = _p.rscratch;
29276 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29277
29278 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29279 return;
29280 } // use 'main' as an alias for the main label (i.e. null prefix)
29281
29282
29283 if (prefix === 'main') {
29284 prefix = null;
29285 }
29286
29287 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29288 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29289 var orgTextX, orgTextY; // used for rotation
29290
29291 var text = this.getLabelText(ele, prefix);
29292
29293 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29294 this.setupTextStyle(context, ele, useEleOpacity);
29295 var pdash = prefix ? prefix + '-' : '';
29296 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29297 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29298 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29299 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29300 var isEdge = ele.isEdge();
29301 var halign = ele.pstyle('text-halign').value;
29302 var valign = ele.pstyle('text-valign').value;
29303
29304 if (isEdge) {
29305 halign = 'center';
29306 valign = 'center';
29307 }
29308
29309 textX += marginX;
29310 textY += marginY;
29311 var theta;
29312
29313 if (!applyRotation) {
29314 theta = 0;
29315 } else {
29316 theta = this.getTextAngle(ele, prefix);
29317 }
29318
29319 if (theta !== 0) {
29320 orgTextX = textX;
29321 orgTextY = textY;
29322 context.translate(orgTextX, orgTextY);
29323 context.rotate(theta);
29324 textX = 0;
29325 textY = 0;
29326 }
29327
29328 switch (valign) {
29329 case 'top':
29330 break;
29331
29332 case 'center':
29333 textY += textH / 2;
29334 break;
29335
29336 case 'bottom':
29337 textY += textH;
29338 break;
29339 }
29340
29341 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29342 var borderOpacity = ele.pstyle('text-border-opacity').value;
29343 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29344 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29345
29346 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29347 var bgX = textX - backgroundPadding;
29348
29349 switch (halign) {
29350 case 'left':
29351 bgX -= textW;
29352 break;
29353
29354 case 'center':
29355 bgX -= textW / 2;
29356 break;
29357 }
29358
29359 var bgY = textY - textH - backgroundPadding;
29360 var bgW = textW + 2 * backgroundPadding;
29361 var bgH = textH + 2 * backgroundPadding;
29362
29363 if (backgroundOpacity > 0) {
29364 var textFill = context.fillStyle;
29365 var textBackgroundColor = ele.pstyle('text-background-color').value;
29366 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29367 var styleShape = ele.pstyle('text-background-shape').strValue;
29368
29369 if (styleShape.indexOf('round') === 0) {
29370 roundRect(context, bgX, bgY, bgW, bgH, 2);
29371 } else {
29372 context.fillRect(bgX, bgY, bgW, bgH);
29373 }
29374
29375 context.fillStyle = textFill;
29376 }
29377
29378 if (textBorderWidth > 0 && borderOpacity > 0) {
29379 var textStroke = context.strokeStyle;
29380 var textLineWidth = context.lineWidth;
29381 var textBorderColor = ele.pstyle('text-border-color').value;
29382 var textBorderStyle = ele.pstyle('text-border-style').value;
29383 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29384 context.lineWidth = textBorderWidth;
29385
29386 if (context.setLineDash) {
29387 // for very outofdate browsers
29388 switch (textBorderStyle) {
29389 case 'dotted':
29390 context.setLineDash([1, 1]);
29391 break;
29392
29393 case 'dashed':
29394 context.setLineDash([4, 2]);
29395 break;
29396
29397 case 'double':
29398 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29399
29400 context.setLineDash([]);
29401 break;
29402
29403 case 'solid':
29404 context.setLineDash([]);
29405 break;
29406 }
29407 }
29408
29409 context.strokeRect(bgX, bgY, bgW, bgH);
29410
29411 if (textBorderStyle === 'double') {
29412 var whiteWidth = textBorderWidth / 2;
29413 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29414 }
29415
29416 if (context.setLineDash) {
29417 // for very outofdate browsers
29418 context.setLineDash([]);
29419 }
29420
29421 context.lineWidth = textLineWidth;
29422 context.strokeStyle = textStroke;
29423 }
29424 }
29425
29426 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29427
29428 if (lineWidth > 0) {
29429 context.lineWidth = lineWidth;
29430 }
29431
29432 if (ele.pstyle('text-wrap').value === 'wrap') {
29433 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29434 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29435 var halfTextW = textW / 2;
29436 var justification = this.getLabelJustification(ele);
29437
29438 if (justification === 'auto') ; else if (halign === 'left') {
29439 // auto justification : right
29440 if (justification === 'left') {
29441 textX += -textW;
29442 } else if (justification === 'center') {
29443 textX += -halfTextW;
29444 } // else same as auto
29445
29446 } else if (halign === 'center') {
29447 // auto justfication : center
29448 if (justification === 'left') {
29449 textX += -halfTextW;
29450 } else if (justification === 'right') {
29451 textX += halfTextW;
29452 } // else same as auto
29453
29454 } else if (halign === 'right') {
29455 // auto justification : left
29456 if (justification === 'center') {
29457 textX += halfTextW;
29458 } else if (justification === 'right') {
29459 textX += textW;
29460 } // else same as auto
29461
29462 }
29463
29464 switch (valign) {
29465 case 'top':
29466 textY -= (lines.length - 1) * lineHeight;
29467 break;
29468
29469 case 'center':
29470 case 'bottom':
29471 textY -= (lines.length - 1) * lineHeight;
29472 break;
29473 }
29474
29475 for (var l = 0; l < lines.length; l++) {
29476 if (lineWidth > 0) {
29477 context.strokeText(lines[l], textX, textY);
29478 }
29479
29480 context.fillText(lines[l], textX, textY);
29481 textY += lineHeight;
29482 }
29483 } else {
29484 if (lineWidth > 0) {
29485 context.strokeText(text, textX, textY);
29486 }
29487
29488 context.fillText(text, textX, textY);
29489 }
29490
29491 if (theta !== 0) {
29492 context.rotate(-theta);
29493 context.translate(-orgTextX, -orgTextY);
29494 }
29495 }
29496};
29497
29498/* global Path2D */
29499var CRp$5 = {};
29500
29501CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29502 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29503 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29504 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29505 var r = this;
29506 var nodeWidth, nodeHeight;
29507 var _p = node._private;
29508 var rs = _p.rscratch;
29509 var pos = node.position();
29510
29511 if (!number(pos.x) || !number(pos.y)) {
29512 return; // can't draw node with undefined position
29513 }
29514
29515 if (shouldDrawOpacity && !node.visible()) {
29516 return;
29517 }
29518
29519 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29520 var usePaths = r.usePaths();
29521 var path;
29522 var pathCacheHit = false;
29523 var padding = node.padding();
29524 nodeWidth = node.width() + 2 * padding;
29525 nodeHeight = node.height() + 2 * padding; //
29526 // setup shift
29527
29528 var bb;
29529
29530 if (shiftToOriginWithBb) {
29531 bb = shiftToOriginWithBb;
29532 context.translate(-bb.x1, -bb.y1);
29533 } //
29534 // load bg image
29535
29536
29537 var bgImgProp = node.pstyle('background-image');
29538 var urls = bgImgProp.value;
29539 var urlDefined = new Array(urls.length);
29540 var image = new Array(urls.length);
29541 var numImages = 0;
29542
29543 for (var i = 0; i < urls.length; i++) {
29544 var url = urls[i];
29545 var defd = urlDefined[i] = url != null && url !== 'none';
29546
29547 if (defd) {
29548 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29549 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29550
29551 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29552 _p.backgroundTimestamp = Date.now();
29553 node.emitAndNotify('background');
29554 });
29555 }
29556 } //
29557 // setup styles
29558
29559
29560 var darkness = node.pstyle('background-blacken').value;
29561 var borderWidth = node.pstyle('border-width').pfValue;
29562 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29563 var borderColor = node.pstyle('border-color').value;
29564 var borderStyle = node.pstyle('border-style').value;
29565 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29566 context.lineJoin = 'miter'; // so borders are square with the node shape
29567
29568 var setupShapeColor = function setupShapeColor() {
29569 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29570 r.eleFillStyle(context, node, bgOpy);
29571 };
29572
29573 var setupBorderColor = function setupBorderColor() {
29574 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29575 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29576 }; //
29577 // setup shape
29578
29579
29580 var styleShape = node.pstyle('shape').strValue;
29581 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29582
29583 if (usePaths) {
29584 context.translate(pos.x, pos.y);
29585 var pathCache = r.nodePathCache = r.nodePathCache || [];
29586 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29587 var cachedPath = pathCache[key];
29588
29589 if (cachedPath != null) {
29590 path = cachedPath;
29591 pathCacheHit = true;
29592 rs.pathCache = path;
29593 } else {
29594 path = new Path2D();
29595 pathCache[key] = rs.pathCache = path;
29596 }
29597 }
29598
29599 var drawShape = function drawShape() {
29600 if (!pathCacheHit) {
29601 var npos = pos;
29602
29603 if (usePaths) {
29604 npos = {
29605 x: 0,
29606 y: 0
29607 };
29608 }
29609
29610 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29611 }
29612
29613 if (usePaths) {
29614 context.fill(path);
29615 } else {
29616 context.fill();
29617 }
29618 };
29619
29620 var drawImages = function drawImages() {
29621 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29622 var prevBging = _p.backgrounding;
29623 var totalCompleted = 0;
29624
29625 for (var _i = 0; _i < image.length; _i++) {
29626 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29627 totalCompleted++;
29628 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29629 }
29630 }
29631
29632 _p.backgrounding = !(totalCompleted === numImages);
29633
29634 if (prevBging !== _p.backgrounding) {
29635 // update style b/c :backgrounding state changed
29636 node.updateStyle(false);
29637 }
29638 };
29639
29640 var drawPie = function drawPie() {
29641 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29642 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29643
29644 if (r.hasPie(node)) {
29645 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29646
29647 if (redrawShape) {
29648 if (!usePaths) {
29649 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29650 }
29651 }
29652 }
29653 };
29654
29655 var darken = function darken() {
29656 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29657 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29658 var c = darkness > 0 ? 0 : 255;
29659
29660 if (darkness !== 0) {
29661 r.colorFillStyle(context, c, c, c, opacity);
29662
29663 if (usePaths) {
29664 context.fill(path);
29665 } else {
29666 context.fill();
29667 }
29668 }
29669 };
29670
29671 var drawBorder = function drawBorder() {
29672 if (borderWidth > 0) {
29673 context.lineWidth = borderWidth;
29674 context.lineCap = 'butt';
29675
29676 if (context.setLineDash) {
29677 // for very outofdate browsers
29678 switch (borderStyle) {
29679 case 'dotted':
29680 context.setLineDash([1, 1]);
29681 break;
29682
29683 case 'dashed':
29684 context.setLineDash([4, 2]);
29685 break;
29686
29687 case 'solid':
29688 case 'double':
29689 context.setLineDash([]);
29690 break;
29691 }
29692 }
29693
29694 if (usePaths) {
29695 context.stroke(path);
29696 } else {
29697 context.stroke();
29698 }
29699
29700 if (borderStyle === 'double') {
29701 context.lineWidth = borderWidth / 3;
29702 var gco = context.globalCompositeOperation;
29703 context.globalCompositeOperation = 'destination-out';
29704
29705 if (usePaths) {
29706 context.stroke(path);
29707 } else {
29708 context.stroke();
29709 }
29710
29711 context.globalCompositeOperation = gco;
29712 } // reset in case we changed the border style
29713
29714
29715 if (context.setLineDash) {
29716 // for very outofdate browsers
29717 context.setLineDash([]);
29718 }
29719 }
29720 };
29721
29722 var drawOverlay = function drawOverlay() {
29723 if (shouldDrawOverlay) {
29724 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
29725 }
29726 };
29727
29728 var drawText = function drawText() {
29729 r.drawElementText(context, node, null, drawLabel);
29730 };
29731
29732 var ghost = node.pstyle('ghost').value === 'yes';
29733
29734 if (ghost) {
29735 var gx = node.pstyle('ghost-offset-x').pfValue;
29736 var gy = node.pstyle('ghost-offset-y').pfValue;
29737 var ghostOpacity = node.pstyle('ghost-opacity').value;
29738 var effGhostOpacity = ghostOpacity * eleOpacity;
29739 context.translate(gx, gy);
29740 setupShapeColor(ghostOpacity * bgOpacity);
29741 drawShape();
29742 drawImages(effGhostOpacity);
29743 drawPie(darkness !== 0 || borderWidth !== 0);
29744 darken(effGhostOpacity);
29745 setupBorderColor(ghostOpacity * borderOpacity);
29746 drawBorder();
29747 context.translate(-gx, -gy);
29748 }
29749
29750 setupShapeColor();
29751 drawShape();
29752 drawImages();
29753 drawPie(darkness !== 0 || borderWidth !== 0);
29754 darken();
29755 setupBorderColor();
29756 drawBorder();
29757
29758 if (usePaths) {
29759 context.translate(-pos.x, -pos.y);
29760 }
29761
29762 drawText();
29763 drawOverlay(); //
29764 // clean up shift
29765
29766 if (shiftToOriginWithBb) {
29767 context.translate(bb.x1, bb.y1);
29768 }
29769};
29770
29771CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
29772 var r = this;
29773
29774 if (!node.visible()) {
29775 return;
29776 }
29777
29778 var overlayPadding = node.pstyle('overlay-padding').pfValue;
29779 var overlayOpacity = node.pstyle('overlay-opacity').value;
29780 var overlayColor = node.pstyle('overlay-color').value;
29781
29782 if (overlayOpacity > 0) {
29783 pos = pos || node.position();
29784
29785 if (nodeWidth == null || nodeHeight == null) {
29786 var padding = node.padding();
29787 nodeWidth = node.width() + 2 * padding;
29788 nodeHeight = node.height() + 2 * padding;
29789 }
29790
29791 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29792 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
29793 context.fill();
29794 }
29795}; // does the node have at least one pie piece?
29796
29797
29798CRp$5.hasPie = function (node) {
29799 node = node[0]; // ensure ele ref
29800
29801 return node._private.hasPie;
29802};
29803
29804CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
29805 node = node[0]; // ensure ele ref
29806
29807 pos = pos || node.position();
29808 var cyStyle = node.cy().style();
29809 var pieSize = node.pstyle('pie-size');
29810 var x = pos.x;
29811 var y = pos.y;
29812 var nodeW = node.width();
29813 var nodeH = node.height();
29814 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
29815
29816 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
29817
29818 var usePaths = this.usePaths();
29819
29820 if (usePaths) {
29821 x = 0;
29822 y = 0;
29823 }
29824
29825 if (pieSize.units === '%') {
29826 radius = radius * pieSize.pfValue;
29827 } else if (pieSize.pfValue !== undefined) {
29828 radius = pieSize.pfValue / 2;
29829 }
29830
29831 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
29832 // 1..N
29833 var size = node.pstyle('pie-' + i + '-background-size').value;
29834 var color = node.pstyle('pie-' + i + '-background-color').value;
29835 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
29836 var percent = size / 100; // map integer range [0, 100] to [0, 1]
29837 // percent can't push beyond 1
29838
29839 if (percent + lastPercent > 1) {
29840 percent = 1 - lastPercent;
29841 }
29842
29843 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
29844
29845 var angleDelta = 2 * Math.PI * percent;
29846 var angleEnd = angleStart + angleDelta; // ignore if
29847 // - zero size
29848 // - we're already beyond the full circle
29849 // - adding the current slice would go beyond the full circle
29850
29851 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
29852 continue;
29853 }
29854
29855 context.beginPath();
29856 context.moveTo(x, y);
29857 context.arc(x, y, radius, angleStart, angleEnd);
29858 context.closePath();
29859 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29860 context.fill();
29861 lastPercent += percent;
29862 }
29863};
29864
29865var CRp$6 = {};
29866var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
29867
29868CRp$6.getPixelRatio = function () {
29869 var context = this.data.contexts[0];
29870
29871 if (this.forcedPixelRatio != null) {
29872 return this.forcedPixelRatio;
29873 }
29874
29875 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
29876 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
29877};
29878
29879CRp$6.paintCache = function (context) {
29880 var caches = this.paintCaches = this.paintCaches || [];
29881 var needToCreateCache = true;
29882 var cache;
29883
29884 for (var i = 0; i < caches.length; i++) {
29885 cache = caches[i];
29886
29887 if (cache.context === context) {
29888 needToCreateCache = false;
29889 break;
29890 }
29891 }
29892
29893 if (needToCreateCache) {
29894 cache = {
29895 context: context
29896 };
29897 caches.push(cache);
29898 }
29899
29900 return cache;
29901};
29902
29903CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
29904 var gradientStyle;
29905 var usePaths = this.usePaths();
29906 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
29907 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
29908
29909 if (fill === 'radial-gradient') {
29910 if (ele.isEdge()) {
29911 var start = ele.sourceEndpoint(),
29912 end = ele.targetEndpoint(),
29913 mid = ele.midpoint();
29914 var d1 = dist(start, mid);
29915 var d2 = dist(end, mid);
29916 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
29917 } else {
29918 var pos = usePaths ? {
29919 x: 0,
29920 y: 0
29921 } : ele.position(),
29922 width = ele.paddedWidth(),
29923 height = ele.paddedHeight();
29924 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
29925 }
29926 } else {
29927 if (ele.isEdge()) {
29928 var _start = ele.sourceEndpoint(),
29929 _end = ele.targetEndpoint();
29930
29931 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
29932 } else {
29933 var _pos = usePaths ? {
29934 x: 0,
29935 y: 0
29936 } : ele.position(),
29937 _width = ele.paddedWidth(),
29938 _height = ele.paddedHeight(),
29939 halfWidth = _width / 2,
29940 halfHeight = _height / 2;
29941
29942 var direction = ele.pstyle('background-gradient-direction').value;
29943
29944 switch (direction) {
29945 case 'to-bottom':
29946 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
29947 break;
29948
29949 case 'to-top':
29950 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
29951 break;
29952
29953 case 'to-left':
29954 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
29955 break;
29956
29957 case 'to-right':
29958 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
29959 break;
29960
29961 case 'to-bottom-right':
29962 case 'to-right-bottom':
29963 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
29964 break;
29965
29966 case 'to-top-right':
29967 case 'to-right-top':
29968 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
29969 break;
29970
29971 case 'to-bottom-left':
29972 case 'to-left-bottom':
29973 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
29974 break;
29975
29976 case 'to-top-left':
29977 case 'to-left-top':
29978 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
29979 break;
29980 }
29981 }
29982 }
29983
29984 if (!gradientStyle) return null; // invalid gradient style
29985
29986 var hasPositions = positions.length === colors.length;
29987 var length = colors.length;
29988
29989 for (var i = 0; i < length; i++) {
29990 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
29991 }
29992
29993 return gradientStyle;
29994};
29995
29996CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
29997 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
29998 if (!gradientStyle) return null; // error
29999
30000 context.fillStyle = gradientStyle;
30001};
30002
30003CRp$6.colorFillStyle = function (context, r, g, b, a) {
30004 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30005 // var cache = this.paintCache(context);
30006 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30007 // if( cache.fillStyle !== fillStyle ){
30008 // context.fillStyle = cache.fillStyle = fillStyle;
30009 // }
30010};
30011
30012CRp$6.eleFillStyle = function (context, ele, opacity) {
30013 var backgroundFill = ele.pstyle('background-fill').value;
30014
30015 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30016 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30017 } else {
30018 var backgroundColor = ele.pstyle('background-color').value;
30019 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30020 }
30021};
30022
30023CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
30024 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30025 if (!gradientStyle) return null; // error
30026
30027 context.strokeStyle = gradientStyle;
30028};
30029
30030CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
30031 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30032 // var cache = this.paintCache(context);
30033 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30034 // if( cache.strokeStyle !== strokeStyle ){
30035 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30036 // }
30037};
30038
30039CRp$6.eleStrokeStyle = function (context, ele, opacity) {
30040 var lineFill = ele.pstyle('line-fill').value;
30041
30042 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30043 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30044 } else {
30045 var lineColor = ele.pstyle('line-color').value;
30046 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30047 }
30048}; // Resize canvas
30049
30050
30051CRp$6.matchCanvasSize = function (container) {
30052 var r = this;
30053 var data = r.data;
30054 var bb = r.findContainerClientCoords();
30055 var width = bb[2];
30056 var height = bb[3];
30057 var pixelRatio = r.getPixelRatio();
30058 var mbPxRatio = r.motionBlurPxRatio;
30059
30060 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30061 pixelRatio = mbPxRatio;
30062 }
30063
30064 var canvasWidth = width * pixelRatio;
30065 var canvasHeight = height * pixelRatio;
30066 var canvas;
30067
30068 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30069 return; // save cycles if same
30070 }
30071
30072 r.fontCaches = null; // resizing resets the style
30073
30074 var canvasContainer = data.canvasContainer;
30075 canvasContainer.style.width = width + 'px';
30076 canvasContainer.style.height = height + 'px';
30077
30078 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30079 canvas = data.canvases[i];
30080 canvas.width = canvasWidth;
30081 canvas.height = canvasHeight;
30082 canvas.style.width = width + 'px';
30083 canvas.style.height = height + 'px';
30084 }
30085
30086 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30087 canvas = data.bufferCanvases[i];
30088 canvas.width = canvasWidth;
30089 canvas.height = canvasHeight;
30090 canvas.style.width = width + 'px';
30091 canvas.style.height = height + 'px';
30092 }
30093
30094 r.textureMult = 1;
30095
30096 if (pixelRatio <= 1) {
30097 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30098 r.textureMult = 2;
30099 canvas.width = canvasWidth * r.textureMult;
30100 canvas.height = canvasHeight * r.textureMult;
30101 }
30102
30103 r.canvasWidth = canvasWidth;
30104 r.canvasHeight = canvasHeight;
30105};
30106
30107CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30108 this.render({
30109 forcedContext: cxt,
30110 forcedZoom: zoom,
30111 forcedPan: pan,
30112 drawAllLayers: true,
30113 forcedPxRatio: pxRatio
30114 });
30115};
30116
30117CRp$6.render = function (options) {
30118 options = options || staticEmptyObject();
30119 var forcedContext = options.forcedContext;
30120 var drawAllLayers = options.drawAllLayers;
30121 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30122 var forcedZoom = options.forcedZoom;
30123 var forcedPan = options.forcedPan;
30124 var r = this;
30125 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30126 var cy = r.cy;
30127 var data = r.data;
30128 var needDraw = data.canvasNeedsRedraw;
30129 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30130 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30131 var mbPxRatio = r.motionBlurPxRatio;
30132 var hasCompoundNodes = cy.hasCompoundNodes();
30133 var inNodeDragGesture = r.hoverData.draggingEles;
30134 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30135 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30136 var motionBlurFadeEffect = motionBlur;
30137
30138 if (!forcedContext) {
30139 if (r.prevPxRatio !== pixelRatio) {
30140 r.invalidateContainerClientCoordsCache();
30141 r.matchCanvasSize(r.container);
30142 r.redrawHint('eles', true);
30143 r.redrawHint('drag', true);
30144 }
30145
30146 r.prevPxRatio = pixelRatio;
30147 }
30148
30149 if (!forcedContext && r.motionBlurTimeout) {
30150 clearTimeout(r.motionBlurTimeout);
30151 }
30152
30153 if (motionBlur) {
30154 if (r.mbFrames == null) {
30155 r.mbFrames = 0;
30156 }
30157
30158 r.mbFrames++;
30159
30160 if (r.mbFrames < 3) {
30161 // need several frames before even high quality motionblur
30162 motionBlurFadeEffect = false;
30163 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30164
30165
30166 if (r.mbFrames > r.minMbLowQualFrames) {
30167 //r.fullQualityMb = false;
30168 r.motionBlurPxRatio = r.mbPxRBlurry;
30169 }
30170 }
30171
30172 if (r.clearingMotionBlur) {
30173 r.motionBlurPxRatio = 1;
30174 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30175 // because a rogue async texture frame would clear needDraw
30176
30177
30178 if (r.textureDrawLastFrame && !textureDraw) {
30179 needDraw[r.NODE] = true;
30180 needDraw[r.SELECT_BOX] = true;
30181 }
30182
30183 var style = cy.style();
30184 var zoom = cy.zoom();
30185 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30186 var pan = cy.pan();
30187 var effectivePan = {
30188 x: pan.x,
30189 y: pan.y
30190 };
30191 var vp = {
30192 zoom: zoom,
30193 pan: {
30194 x: pan.x,
30195 y: pan.y
30196 }
30197 };
30198 var prevVp = r.prevViewport;
30199 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)
30200
30201 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30202 r.motionBlurPxRatio = 1;
30203 }
30204
30205 if (forcedPan) {
30206 effectivePan = forcedPan;
30207 } // apply pixel ratio
30208
30209
30210 effectiveZoom *= pixelRatio;
30211 effectivePan.x *= pixelRatio;
30212 effectivePan.y *= pixelRatio;
30213 var eles = r.getCachedZSortedEles();
30214
30215 function mbclear(context, x, y, w, h) {
30216 var gco = context.globalCompositeOperation;
30217 context.globalCompositeOperation = 'destination-out';
30218 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30219 context.fillRect(x, y, w, h);
30220 context.globalCompositeOperation = gco;
30221 }
30222
30223 function setContextTransform(context, clear) {
30224 var ePan, eZoom, w, h;
30225
30226 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30227 ePan = {
30228 x: pan.x * mbPxRatio,
30229 y: pan.y * mbPxRatio
30230 };
30231 eZoom = zoom * mbPxRatio;
30232 w = r.canvasWidth * mbPxRatio;
30233 h = r.canvasHeight * mbPxRatio;
30234 } else {
30235 ePan = effectivePan;
30236 eZoom = effectiveZoom;
30237 w = r.canvasWidth;
30238 h = r.canvasHeight;
30239 }
30240
30241 context.setTransform(1, 0, 0, 1, 0, 0);
30242
30243 if (clear === 'motionBlur') {
30244 mbclear(context, 0, 0, w, h);
30245 } else if (!forcedContext && (clear === undefined || clear)) {
30246 context.clearRect(0, 0, w, h);
30247 }
30248
30249 if (!drawAllLayers) {
30250 context.translate(ePan.x, ePan.y);
30251 context.scale(eZoom, eZoom);
30252 }
30253
30254 if (forcedPan) {
30255 context.translate(forcedPan.x, forcedPan.y);
30256 }
30257
30258 if (forcedZoom) {
30259 context.scale(forcedZoom, forcedZoom);
30260 }
30261 }
30262
30263 if (!textureDraw) {
30264 r.textureDrawLastFrame = false;
30265 }
30266
30267 if (textureDraw) {
30268 r.textureDrawLastFrame = true;
30269
30270 if (!r.textureCache) {
30271 r.textureCache = {};
30272 r.textureCache.bb = cy.mutableElements().boundingBox();
30273 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30274 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30275 cxt.setTransform(1, 0, 0, 1, 0, 0);
30276 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30277 r.render({
30278 forcedContext: cxt,
30279 drawOnlyNodeLayer: true,
30280 forcedPxRatio: pixelRatio * r.textureMult
30281 });
30282 var vp = r.textureCache.viewport = {
30283 zoom: cy.zoom(),
30284 pan: cy.pan(),
30285 width: r.canvasWidth,
30286 height: r.canvasHeight
30287 };
30288 vp.mpan = {
30289 x: (0 - vp.pan.x) / vp.zoom,
30290 y: (0 - vp.pan.y) / vp.zoom
30291 };
30292 }
30293
30294 needDraw[r.DRAG] = false;
30295 needDraw[r.NODE] = false;
30296 var context = data.contexts[r.NODE];
30297 var texture = r.textureCache.texture;
30298 var vp = r.textureCache.viewport;
30299 context.setTransform(1, 0, 0, 1, 0, 0);
30300
30301 if (motionBlur) {
30302 mbclear(context, 0, 0, vp.width, vp.height);
30303 } else {
30304 context.clearRect(0, 0, vp.width, vp.height);
30305 }
30306
30307 var outsideBgColor = style.core('outside-texture-bg-color').value;
30308 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30309 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30310 context.fillRect(0, 0, vp.width, vp.height);
30311 var zoom = cy.zoom();
30312 setContextTransform(context, false);
30313 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30314 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30315 } else if (r.textureOnViewport && !forcedContext) {
30316 // clear the cache since we don't need it
30317 r.textureCache = null;
30318 }
30319
30320 var extent = cy.extent();
30321 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30322 var hideEdges = r.hideEdgesOnViewport && vpManip;
30323 var needMbClear = [];
30324 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30325
30326 if (needMbClear[r.NODE]) {
30327 r.clearedForMotionBlur[r.NODE] = true;
30328 }
30329
30330 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30331
30332 if (needMbClear[r.DRAG]) {
30333 r.clearedForMotionBlur[r.DRAG] = true;
30334 }
30335
30336 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30337 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30338 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30339 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30340 setContextTransform(context, clear);
30341
30342 if (hideEdges) {
30343 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30344 } else {
30345 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30346 }
30347
30348 if (r.debug) {
30349 r.drawDebugPoints(context, eles.nondrag);
30350 }
30351
30352 if (!drawAllLayers && !motionBlur) {
30353 needDraw[r.NODE] = false;
30354 }
30355 }
30356
30357 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30358 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30359 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30360 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30361
30362 if (hideEdges) {
30363 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30364 } else {
30365 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30366 }
30367
30368 if (r.debug) {
30369 r.drawDebugPoints(context, eles.drag);
30370 }
30371
30372 if (!drawAllLayers && !motionBlur) {
30373 needDraw[r.DRAG] = false;
30374 }
30375 }
30376
30377 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30378 var context = forcedContext || data.contexts[r.SELECT_BOX];
30379 setContextTransform(context);
30380
30381 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30382 var zoom = r.cy.zoom();
30383 var borderWidth = style.core('selection-box-border-width').value / zoom;
30384 context.lineWidth = borderWidth;
30385 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 + ')';
30386 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30387
30388 if (borderWidth > 0) {
30389 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 + ')';
30390 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30391 }
30392 }
30393
30394 if (data.bgActivePosistion && !r.hoverData.selecting) {
30395 var zoom = r.cy.zoom();
30396 var pos = data.bgActivePosistion;
30397 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 + ')';
30398 context.beginPath();
30399 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30400 context.fill();
30401 }
30402
30403 var timeToRender = r.lastRedrawTime;
30404
30405 if (r.showFps && timeToRender) {
30406 timeToRender = Math.round(timeToRender);
30407 var fps = Math.round(1000 / timeToRender);
30408 context.setTransform(1, 0, 0, 1, 0, 0);
30409 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30410 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30411 context.lineWidth = 1;
30412 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30413 var maxFps = 60;
30414 context.strokeRect(0, 30, 250, 20);
30415 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30416 }
30417
30418 if (!drawAllLayers) {
30419 needDraw[r.SELECT_BOX] = false;
30420 }
30421 } // motionblur: blit rendered blurry frames
30422
30423
30424 if (motionBlur && mbPxRatio !== 1) {
30425 var cxtNode = data.contexts[r.NODE];
30426 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30427 var cxtDrag = data.contexts[r.DRAG];
30428 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30429
30430 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30431 cxt.setTransform(1, 0, 0, 1, 0, 0);
30432
30433 if (needClear || !motionBlurFadeEffect) {
30434 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30435 } else {
30436 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30437 }
30438
30439 var pxr = mbPxRatio;
30440 cxt.drawImage(txt, // img
30441 0, 0, // sx, sy
30442 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30443 0, 0, // x, y
30444 r.canvasWidth, r.canvasHeight // w, h
30445 );
30446 };
30447
30448 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30449 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30450 needDraw[r.NODE] = false;
30451 }
30452
30453 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30454 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30455 needDraw[r.DRAG] = false;
30456 }
30457 }
30458
30459 r.prevViewport = vp;
30460
30461 if (r.clearingMotionBlur) {
30462 r.clearingMotionBlur = false;
30463 r.motionBlurCleared = true;
30464 r.motionBlur = true;
30465 }
30466
30467 if (motionBlur) {
30468 r.motionBlurTimeout = setTimeout(function () {
30469 r.motionBlurTimeout = null;
30470 r.clearedForMotionBlur[r.NODE] = false;
30471 r.clearedForMotionBlur[r.DRAG] = false;
30472 r.motionBlur = false;
30473 r.clearingMotionBlur = !textureDraw;
30474 r.mbFrames = 0;
30475 needDraw[r.NODE] = true;
30476 needDraw[r.DRAG] = true;
30477 r.redraw();
30478 }, motionBlurDelay);
30479 }
30480
30481 if (!forcedContext) {
30482 cy.emit('render');
30483 }
30484};
30485
30486var CRp$7 = {}; // @O Polygon drawing
30487
30488CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
30489 var halfW = width / 2;
30490 var halfH = height / 2;
30491
30492 if (context.beginPath) {
30493 context.beginPath();
30494 }
30495
30496 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30497
30498 for (var i = 1; i < points.length / 2; i++) {
30499 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30500 }
30501
30502 context.closePath();
30503};
30504
30505CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30506 var halfW = width / 2;
30507 var halfH = height / 2;
30508 var cornerRadius = getRoundPolygonRadius(width, height);
30509
30510 if (context.beginPath) {
30511 context.beginPath();
30512 }
30513
30514 for (var _i = 0; _i < points.length / 4; _i++) {
30515 var sourceUv = void 0,
30516 destUv = void 0;
30517
30518 if (_i === 0) {
30519 sourceUv = points.length - 2;
30520 } else {
30521 sourceUv = _i * 4 - 2;
30522 }
30523
30524 destUv = _i * 4 + 2;
30525 var px = x + halfW * points[_i * 4];
30526 var py = y + halfH * points[_i * 4 + 1];
30527 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30528 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30529 var cp0x = px - offset * points[sourceUv];
30530 var cp0y = py - offset * points[sourceUv + 1];
30531 var cp1x = px + offset * points[destUv];
30532 var cp1y = py + offset * points[destUv + 1];
30533
30534 if (_i === 0) {
30535 context.moveTo(cp0x, cp0y);
30536 } else {
30537 context.lineTo(cp0x, cp0y);
30538 }
30539
30540 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30541 }
30542
30543 context.closePath();
30544}; // Round rectangle drawing
30545
30546
30547CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
30548 var halfWidth = width / 2;
30549 var halfHeight = height / 2;
30550 var cornerRadius = getRoundRectangleRadius(width, height);
30551
30552 if (context.beginPath) {
30553 context.beginPath();
30554 } // Start at top middle
30555
30556
30557 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30558
30559 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30560
30561 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30562
30563 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30564
30565 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30566
30567 context.lineTo(x, y - halfHeight);
30568 context.closePath();
30569};
30570
30571CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30572 var halfWidth = width / 2;
30573 var halfHeight = height / 2;
30574 var cornerRadius = getRoundRectangleRadius(width, height);
30575
30576 if (context.beginPath) {
30577 context.beginPath();
30578 } // Start at top middle
30579
30580
30581 context.moveTo(x, y - halfHeight);
30582 context.lineTo(x + halfWidth, y - halfHeight);
30583 context.lineTo(x + halfWidth, y);
30584 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30585 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30586 context.lineTo(x - halfWidth, y - halfHeight);
30587 context.lineTo(x, y - halfHeight);
30588 context.closePath();
30589};
30590
30591CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
30592 var halfWidth = width / 2;
30593 var halfHeight = height / 2;
30594 var cornerLength = getCutRectangleCornerLength();
30595
30596 if (context.beginPath) {
30597 context.beginPath();
30598 }
30599
30600 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30601 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30602 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30603 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30604 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30605 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30606 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30607 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30608 context.closePath();
30609};
30610
30611CRp$7.drawBarrelPath = function (context, x, y, width, height) {
30612 var halfWidth = width / 2;
30613 var halfHeight = height / 2;
30614 var xBegin = x - halfWidth;
30615 var xEnd = x + halfWidth;
30616 var yBegin = y - halfHeight;
30617 var yEnd = y + halfHeight;
30618 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30619 var wOffset = barrelCurveConstants.widthOffset;
30620 var hOffset = barrelCurveConstants.heightOffset;
30621 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30622
30623 if (context.beginPath) {
30624 context.beginPath();
30625 }
30626
30627 context.moveTo(xBegin, yBegin + hOffset);
30628 context.lineTo(xBegin, yEnd - hOffset);
30629 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30630 context.lineTo(xEnd - wOffset, yEnd);
30631 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30632 context.lineTo(xEnd, yBegin + hOffset);
30633 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30634 context.lineTo(xBegin + wOffset, yBegin);
30635 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30636 context.closePath();
30637};
30638
30639var sin0 = Math.sin(0);
30640var cos0 = Math.cos(0);
30641var sin = {};
30642var cos = {};
30643var ellipseStepSize = Math.PI / 40;
30644
30645for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30646 sin[i] = Math.sin(i);
30647 cos[i] = Math.cos(i);
30648}
30649
30650CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
30651 if (context.beginPath) {
30652 context.beginPath();
30653 }
30654
30655 if (context.ellipse) {
30656 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30657 } else {
30658 var xPos, yPos;
30659 var rw = width / 2;
30660 var rh = height / 2;
30661
30662 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30663 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30664 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30665
30666 if (i === 0) {
30667 context.moveTo(xPos, yPos);
30668 } else {
30669 context.lineTo(xPos, yPos);
30670 }
30671 }
30672 }
30673
30674 context.closePath();
30675};
30676
30677/* global atob, ArrayBuffer, Uint8Array, Blob */
30678var CRp$8 = {};
30679
30680CRp$8.createBuffer = function (w, h) {
30681 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30682
30683 buffer.width = w;
30684 buffer.height = h;
30685 return [buffer, buffer.getContext('2d')];
30686};
30687
30688CRp$8.bufferCanvasImage = function (options) {
30689 var cy = this.cy;
30690 var eles = cy.mutableElements();
30691 var bb = eles.boundingBox();
30692 var ctrRect = this.findContainerClientCoords();
30693 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
30694 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
30695 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
30696 var pxRatio = this.getPixelRatio();
30697 var scale = 1;
30698
30699 if (options.scale !== undefined) {
30700 width *= options.scale;
30701 height *= options.scale;
30702 scale = options.scale;
30703 } else if (specdMaxDims) {
30704 var maxScaleW = Infinity;
30705 var maxScaleH = Infinity;
30706
30707 if (number(options.maxWidth)) {
30708 maxScaleW = scale * options.maxWidth / width;
30709 }
30710
30711 if (number(options.maxHeight)) {
30712 maxScaleH = scale * options.maxHeight / height;
30713 }
30714
30715 scale = Math.min(maxScaleW, maxScaleH);
30716 width *= scale;
30717 height *= scale;
30718 }
30719
30720 if (!specdMaxDims) {
30721 width *= pxRatio;
30722 height *= pxRatio;
30723 scale *= pxRatio;
30724 }
30725
30726 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
30727
30728 buffCanvas.width = width;
30729 buffCanvas.height = height;
30730 buffCanvas.style.width = width + 'px';
30731 buffCanvas.style.height = height + 'px';
30732 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
30733
30734 if (width > 0 && height > 0) {
30735 buffCxt.clearRect(0, 0, width, height);
30736 buffCxt.globalCompositeOperation = 'source-over';
30737 var zsortedEles = this.getCachedZSortedEles();
30738
30739 if (options.full) {
30740 // draw the full bounds of the graph
30741 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
30742 buffCxt.scale(scale, scale);
30743 this.drawElements(buffCxt, zsortedEles);
30744 buffCxt.scale(1 / scale, 1 / scale);
30745 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
30746 } else {
30747 // draw the current view
30748 var pan = cy.pan();
30749 var translation = {
30750 x: pan.x * scale,
30751 y: pan.y * scale
30752 };
30753 scale *= cy.zoom();
30754 buffCxt.translate(translation.x, translation.y);
30755 buffCxt.scale(scale, scale);
30756 this.drawElements(buffCxt, zsortedEles);
30757 buffCxt.scale(1 / scale, 1 / scale);
30758 buffCxt.translate(-translation.x, -translation.y);
30759 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
30760
30761
30762 if (options.bg) {
30763 buffCxt.globalCompositeOperation = 'destination-over';
30764 buffCxt.fillStyle = options.bg;
30765 buffCxt.rect(0, 0, width, height);
30766 buffCxt.fill();
30767 }
30768 }
30769
30770 return buffCanvas;
30771};
30772
30773function b64ToBlob(b64, mimeType) {
30774 var bytes = atob(b64);
30775 var buff = new ArrayBuffer(bytes.length);
30776 var buffUint8 = new Uint8Array(buff);
30777
30778 for (var i = 0; i < bytes.length; i++) {
30779 buffUint8[i] = bytes.charCodeAt(i);
30780 }
30781
30782 return new Blob([buff], {
30783 type: mimeType
30784 });
30785}
30786
30787function b64UriToB64(b64uri) {
30788 var i = b64uri.indexOf(',');
30789 return b64uri.substr(i + 1);
30790}
30791
30792function output(options, canvas, mimeType) {
30793 var getB64Uri = function getB64Uri() {
30794 return canvas.toDataURL(mimeType, options.quality);
30795 };
30796
30797 switch (options.output) {
30798 case 'blob-promise':
30799 return new Promise$1(function (resolve, reject) {
30800 try {
30801 canvas.toBlob(function (blob) {
30802 if (blob != null) {
30803 resolve(blob);
30804 } else {
30805 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
30806 }
30807 }, mimeType, options.quality);
30808 } catch (err) {
30809 reject(err);
30810 }
30811 });
30812
30813 case 'blob':
30814 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
30815
30816 case 'base64':
30817 return b64UriToB64(getB64Uri());
30818
30819 case 'base64uri':
30820 default:
30821 return getB64Uri();
30822 }
30823}
30824
30825CRp$8.png = function (options) {
30826 return output(options, this.bufferCanvasImage(options), 'image/png');
30827};
30828
30829CRp$8.jpg = function (options) {
30830 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
30831};
30832
30833var CRp$9 = {};
30834
30835CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
30836 switch (name) {
30837 case 'ellipse':
30838 return this.drawEllipsePath(context, centerX, centerY, width, height);
30839
30840 case 'polygon':
30841 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
30842
30843 case 'round-polygon':
30844 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
30845
30846 case 'roundrectangle':
30847 case 'round-rectangle':
30848 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
30849
30850 case 'cutrectangle':
30851 case 'cut-rectangle':
30852 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
30853
30854 case 'bottomroundrectangle':
30855 case 'bottom-round-rectangle':
30856 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
30857
30858 case 'barrel':
30859 return this.drawBarrelPath(context, centerX, centerY, width, height);
30860 }
30861};
30862
30863var CR = CanvasRenderer;
30864var CRp$a = CanvasRenderer.prototype;
30865CRp$a.CANVAS_LAYERS = 3; //
30866
30867CRp$a.SELECT_BOX = 0;
30868CRp$a.DRAG = 1;
30869CRp$a.NODE = 2;
30870CRp$a.BUFFER_COUNT = 3; //
30871
30872CRp$a.TEXTURE_BUFFER = 0;
30873CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
30874CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
30875
30876function CanvasRenderer(options) {
30877 var r = this;
30878 r.data = {
30879 canvases: new Array(CRp$a.CANVAS_LAYERS),
30880 contexts: new Array(CRp$a.CANVAS_LAYERS),
30881 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
30882 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
30883 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
30884 };
30885 var tapHlOffAttr = '-webkit-tap-highlight-color';
30886 var tapHlOffStyle = 'rgba(0,0,0,0)';
30887 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
30888
30889 var containerStyle = r.data.canvasContainer.style;
30890 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
30891 containerStyle.position = 'relative';
30892 containerStyle.zIndex = '0';
30893 containerStyle.overflow = 'hidden';
30894 var container = options.cy.container();
30895 container.appendChild(r.data.canvasContainer);
30896 container.style[tapHlOffAttr] = tapHlOffStyle;
30897 var styleMap = {
30898 '-webkit-user-select': 'none',
30899 '-moz-user-select': '-moz-none',
30900 'user-select': 'none',
30901 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
30902 'outline-style': 'none'
30903 };
30904
30905 if (ms()) {
30906 styleMap['-ms-touch-action'] = 'none';
30907 styleMap['touch-action'] = 'none';
30908 }
30909
30910 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
30911 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30912
30913 r.data.contexts[i] = canvas.getContext('2d');
30914 Object.keys(styleMap).forEach(function (k) {
30915 canvas.style[k] = styleMap[k];
30916 });
30917 canvas.style.position = 'absolute';
30918 canvas.setAttribute('data-id', 'layer' + i);
30919 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
30920 r.data.canvasContainer.appendChild(canvas);
30921 r.data.canvasNeedsRedraw[i] = false;
30922 }
30923
30924 r.data.topCanvas = r.data.canvases[0];
30925 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
30926 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
30927 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
30928
30929 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
30930 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30931
30932 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
30933 r.data.bufferCanvases[i].style.position = 'absolute';
30934 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
30935 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
30936 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
30937 }
30938
30939 r.pathsEnabled = true;
30940 var emptyBb = makeBoundingBox();
30941
30942 var getBoxCenter = function getBoxCenter(bb) {
30943 return {
30944 x: (bb.x1 + bb.x2) / 2,
30945 y: (bb.y1 + bb.y2) / 2
30946 };
30947 };
30948
30949 var getCenterOffset = function getCenterOffset(bb) {
30950 return {
30951 x: -bb.w / 2,
30952 y: -bb.h / 2
30953 };
30954 };
30955
30956 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
30957 var _p = ele[0]._private;
30958 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
30959 return !same;
30960 };
30961
30962 var getStyleKey = function getStyleKey(ele) {
30963 return ele[0]._private.nodeKey;
30964 };
30965
30966 var getLabelKey = function getLabelKey(ele) {
30967 return ele[0]._private.labelStyleKey;
30968 };
30969
30970 var getSourceLabelKey = function getSourceLabelKey(ele) {
30971 return ele[0]._private.sourceLabelStyleKey;
30972 };
30973
30974 var getTargetLabelKey = function getTargetLabelKey(ele) {
30975 return ele[0]._private.targetLabelStyleKey;
30976 };
30977
30978 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
30979 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
30980 };
30981
30982 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30983 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
30984 };
30985
30986 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30987 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
30988 };
30989
30990 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30991 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
30992 };
30993
30994 var getElementBox = function getElementBox(ele) {
30995 ele.boundingBox();
30996 return ele[0]._private.bodyBounds;
30997 };
30998
30999 var getLabelBox = function getLabelBox(ele) {
31000 ele.boundingBox();
31001 return ele[0]._private.labelBounds.main || emptyBb;
31002 };
31003
31004 var getSourceLabelBox = function getSourceLabelBox(ele) {
31005 ele.boundingBox();
31006 return ele[0]._private.labelBounds.source || emptyBb;
31007 };
31008
31009 var getTargetLabelBox = function getTargetLabelBox(ele) {
31010 ele.boundingBox();
31011 return ele[0]._private.labelBounds.target || emptyBb;
31012 };
31013
31014 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31015 return scaledLabelShown;
31016 };
31017
31018 var getElementRotationPoint = function getElementRotationPoint(ele) {
31019 return getBoxCenter(getElementBox(ele));
31020 };
31021
31022 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31023 var pre = prefix ? prefix + '-' : '';
31024 return {
31025 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31026 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31027 };
31028 };
31029
31030 var getRsPt = function getRsPt(ele, x, y) {
31031 var rs = ele[0]._private.rscratch;
31032 return {
31033 x: rs[x],
31034 y: rs[y]
31035 };
31036 };
31037
31038 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31039 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31040 };
31041
31042 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31043 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31044 };
31045
31046 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31047 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31048 };
31049
31050 var getElementRotationOffset = function getElementRotationOffset(ele) {
31051 return getCenterOffset(getElementBox(ele));
31052 };
31053
31054 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31055 return getCenterOffset(getSourceLabelBox(ele));
31056 };
31057
31058 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31059 return getCenterOffset(getTargetLabelBox(ele));
31060 };
31061
31062 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31063 var bb = getLabelBox(ele);
31064 var p = getCenterOffset(getLabelBox(ele));
31065
31066 if (ele.isNode()) {
31067 switch (ele.pstyle('text-halign').value) {
31068 case 'left':
31069 p.x = -bb.w;
31070 break;
31071
31072 case 'right':
31073 p.x = 0;
31074 break;
31075 }
31076
31077 switch (ele.pstyle('text-valign').value) {
31078 case 'top':
31079 p.y = -bb.h;
31080 break;
31081
31082 case 'bottom':
31083 p.y = 0;
31084 break;
31085 }
31086 }
31087
31088 return p;
31089 };
31090
31091 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31092 getKey: getStyleKey,
31093 doesEleInvalidateKey: backgroundTimestampHasChanged,
31094 drawElement: drawElement,
31095 getBoundingBox: getElementBox,
31096 getRotationPoint: getElementRotationPoint,
31097 getRotationOffset: getElementRotationOffset,
31098 allowEdgeTxrCaching: false,
31099 allowParentTxrCaching: false
31100 });
31101 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31102 getKey: getLabelKey,
31103 drawElement: drawLabel,
31104 getBoundingBox: getLabelBox,
31105 getRotationPoint: getLabelRotationPoint,
31106 getRotationOffset: getLabelRotationOffset,
31107 isVisible: isLabelVisibleAtScale
31108 });
31109 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31110 getKey: getSourceLabelKey,
31111 drawElement: drawSourceLabel,
31112 getBoundingBox: getSourceLabelBox,
31113 getRotationPoint: getSourceLabelRotationPoint,
31114 getRotationOffset: getSourceLabelRotationOffset,
31115 isVisible: isLabelVisibleAtScale
31116 });
31117 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31118 getKey: getTargetLabelKey,
31119 drawElement: drawTargetLabel,
31120 getBoundingBox: getTargetLabelBox,
31121 getRotationPoint: getTargetLabelRotationPoint,
31122 getRotationOffset: getTargetLabelRotationOffset,
31123 isVisible: isLabelVisibleAtScale
31124 });
31125 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31126 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31127 // each cache should check for sub-key diff to see that the update affects that cache particularly
31128 eleTxrCache.invalidateElements(eles);
31129 lblTxrCache.invalidateElements(eles);
31130 slbTxrCache.invalidateElements(eles);
31131 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31132
31133 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31134
31135 for (var _i = 0; _i < eles.length; _i++) {
31136 var _p = eles[_i]._private;
31137 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31138 }
31139 });
31140
31141 var refineInLayers = function refineInLayers(reqs) {
31142 for (var i = 0; i < reqs.length; i++) {
31143 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31144 }
31145 };
31146
31147 eleTxrCache.onDequeue(refineInLayers);
31148 lblTxrCache.onDequeue(refineInLayers);
31149 slbTxrCache.onDequeue(refineInLayers);
31150 tlbTxrCache.onDequeue(refineInLayers);
31151}
31152
31153CRp$a.redrawHint = function (group, bool) {
31154 var r = this;
31155
31156 switch (group) {
31157 case 'eles':
31158 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31159 break;
31160
31161 case 'drag':
31162 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31163 break;
31164
31165 case 'select':
31166 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31167 break;
31168 }
31169}; // whether to use Path2D caching for drawing
31170
31171
31172var pathsImpld = typeof Path2D !== 'undefined';
31173
31174CRp$a.path2dEnabled = function (on) {
31175 if (on === undefined) {
31176 return this.pathsEnabled;
31177 }
31178
31179 this.pathsEnabled = on ? true : false;
31180};
31181
31182CRp$a.usePaths = function () {
31183 return pathsImpld && this.pathsEnabled;
31184};
31185
31186CRp$a.setImgSmoothing = function (context, bool) {
31187 if (context.imageSmoothingEnabled != null) {
31188 context.imageSmoothingEnabled = bool;
31189 } else {
31190 context.webkitImageSmoothingEnabled = bool;
31191 context.mozImageSmoothingEnabled = bool;
31192 context.msImageSmoothingEnabled = bool;
31193 }
31194};
31195
31196CRp$a.getImgSmoothing = function (context) {
31197 if (context.imageSmoothingEnabled != null) {
31198 return context.imageSmoothingEnabled;
31199 } else {
31200 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31201 }
31202};
31203
31204CRp$a.makeOffscreenCanvas = function (width, height) {
31205 var canvas;
31206
31207 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
31208 canvas = new OffscreenCanvas(width, height);
31209 } else {
31210 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31211
31212 canvas.width = width;
31213 canvas.height = height;
31214 }
31215
31216 return canvas;
31217};
31218
31219[CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
31220 extend(CRp$a, props);
31221});
31222
31223var renderer = [{
31224 name: 'null',
31225 impl: NullRenderer
31226}, {
31227 name: 'base',
31228 impl: BR
31229}, {
31230 name: 'canvas',
31231 impl: CR
31232}];
31233
31234var incExts = [{
31235 type: 'layout',
31236 extensions: layout
31237}, {
31238 type: 'renderer',
31239 extensions: renderer
31240}];
31241
31242var extensions = {}; // registered modules for extensions, indexed by name
31243
31244var modules = {};
31245
31246function setExtension(type, name, registrant) {
31247 var ext = registrant;
31248
31249 var overrideErr = function overrideErr(field) {
31250 error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31251 };
31252
31253 if (type === 'core') {
31254 if (Core.prototype[name]) {
31255 return overrideErr(name);
31256 } else {
31257 Core.prototype[name] = registrant;
31258 }
31259 } else if (type === 'collection') {
31260 if (Collection.prototype[name]) {
31261 return overrideErr(name);
31262 } else {
31263 Collection.prototype[name] = registrant;
31264 }
31265 } else if (type === 'layout') {
31266 // fill in missing layout functions in the prototype
31267 var Layout = function Layout(options) {
31268 this.options = options;
31269 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31270
31271 if (!plainObject(this._private)) {
31272 this._private = {};
31273 }
31274
31275 this._private.cy = options.cy;
31276 this._private.listeners = [];
31277 this.createEmitter();
31278 };
31279
31280 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31281 var optLayoutFns = [];
31282
31283 for (var i = 0; i < optLayoutFns.length; i++) {
31284 var fnName = optLayoutFns[i];
31285
31286 layoutProto[fnName] = layoutProto[fnName] || function () {
31287 return this;
31288 };
31289 } // either .start() or .run() is defined, so autogen the other
31290
31291
31292 if (layoutProto.start && !layoutProto.run) {
31293 layoutProto.run = function () {
31294 this.start();
31295 return this;
31296 };
31297 } else if (!layoutProto.start && layoutProto.run) {
31298 layoutProto.start = function () {
31299 this.run();
31300 return this;
31301 };
31302 }
31303
31304 var regStop = registrant.prototype.stop;
31305
31306 layoutProto.stop = function () {
31307 var opts = this.options;
31308
31309 if (opts && opts.animate) {
31310 var anis = this.animations;
31311
31312 if (anis) {
31313 for (var _i = 0; _i < anis.length; _i++) {
31314 anis[_i].stop();
31315 }
31316 }
31317 }
31318
31319 if (regStop) {
31320 regStop.call(this);
31321 } else {
31322 this.emit('layoutstop');
31323 }
31324
31325 return this;
31326 };
31327
31328 if (!layoutProto.destroy) {
31329 layoutProto.destroy = function () {
31330 return this;
31331 };
31332 }
31333
31334 layoutProto.cy = function () {
31335 return this._private.cy;
31336 };
31337
31338 var getCy = function getCy(layout) {
31339 return layout._private.cy;
31340 };
31341
31342 var emitterOpts = {
31343 addEventFields: function addEventFields(layout, evt) {
31344 evt.layout = layout;
31345 evt.cy = getCy(layout);
31346 evt.target = layout;
31347 },
31348 bubble: function bubble() {
31349 return true;
31350 },
31351 parent: function parent(layout) {
31352 return getCy(layout);
31353 }
31354 };
31355 extend(layoutProto, {
31356 createEmitter: function createEmitter() {
31357 this._private.emitter = new Emitter(emitterOpts, this);
31358 return this;
31359 },
31360 emitter: function emitter() {
31361 return this._private.emitter;
31362 },
31363 on: function on(evt, cb) {
31364 this.emitter().on(evt, cb);
31365 return this;
31366 },
31367 one: function one(evt, cb) {
31368 this.emitter().one(evt, cb);
31369 return this;
31370 },
31371 once: function once(evt, cb) {
31372 this.emitter().one(evt, cb);
31373 return this;
31374 },
31375 removeListener: function removeListener(evt, cb) {
31376 this.emitter().removeListener(evt, cb);
31377 return this;
31378 },
31379 removeAllListeners: function removeAllListeners() {
31380 this.emitter().removeAllListeners();
31381 return this;
31382 },
31383 emit: function emit(evt, params) {
31384 this.emitter().emit(evt, params);
31385 return this;
31386 }
31387 });
31388 define$3.eventAliasesOn(layoutProto);
31389 ext = Layout; // replace with our wrapped layout
31390 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31391 // user registered renderers inherit from base
31392 var BaseRenderer = getExtension('renderer', 'base');
31393 var bProto = BaseRenderer.prototype;
31394 var RegistrantRenderer = registrant;
31395 var rProto = registrant.prototype;
31396
31397 var Renderer = function Renderer() {
31398 BaseRenderer.apply(this, arguments);
31399 RegistrantRenderer.apply(this, arguments);
31400 };
31401
31402 var proto = Renderer.prototype;
31403
31404 for (var pName in bProto) {
31405 var pVal = bProto[pName];
31406 var existsInR = rProto[pName] != null;
31407
31408 if (existsInR) {
31409 return overrideErr(pName);
31410 }
31411
31412 proto[pName] = pVal; // take impl from base
31413 }
31414
31415 for (var _pName in rProto) {
31416 proto[_pName] = rProto[_pName]; // take impl from registrant
31417 }
31418
31419 bProto.clientFunctions.forEach(function (name) {
31420 proto[name] = proto[name] || function () {
31421 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31422 };
31423 });
31424 ext = Renderer;
31425 }
31426
31427 return setMap({
31428 map: extensions,
31429 keys: [type, name],
31430 value: ext
31431 });
31432}
31433
31434function getExtension(type, name) {
31435 return getMap({
31436 map: extensions,
31437 keys: [type, name]
31438 });
31439}
31440
31441function setModule(type, name, moduleType, moduleName, registrant) {
31442 return setMap({
31443 map: modules,
31444 keys: [type, name, moduleType, moduleName],
31445 value: registrant
31446 });
31447}
31448
31449function getModule(type, name, moduleType, moduleName) {
31450 return getMap({
31451 map: modules,
31452 keys: [type, name, moduleType, moduleName]
31453 });
31454}
31455
31456var extension = function extension() {
31457 // e.g. extension('renderer', 'svg')
31458 if (arguments.length === 2) {
31459 return getExtension.apply(null, arguments);
31460 } // e.g. extension('renderer', 'svg', { ... })
31461 else if (arguments.length === 3) {
31462 return setExtension.apply(null, arguments);
31463 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31464 else if (arguments.length === 4) {
31465 return getModule.apply(null, arguments);
31466 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31467 else if (arguments.length === 5) {
31468 return setModule.apply(null, arguments);
31469 } else {
31470 error('Invalid extension access syntax');
31471 }
31472}; // allows a core instance to access extensions internally
31473
31474
31475Core.prototype.extension = extension; // included extensions
31476
31477incExts.forEach(function (group) {
31478 group.extensions.forEach(function (ext) {
31479 setExtension(group.type, ext.name, ext.impl);
31480 });
31481});
31482
31483// (useful for init)
31484
31485var Stylesheet = function Stylesheet() {
31486 if (!(this instanceof Stylesheet)) {
31487 return new Stylesheet();
31488 }
31489
31490 this.length = 0;
31491};
31492
31493var sheetfn = Stylesheet.prototype;
31494
31495sheetfn.instanceString = function () {
31496 return 'stylesheet';
31497}; // just store the selector to be parsed later
31498
31499
31500sheetfn.selector = function (selector) {
31501 var i = this.length++;
31502 this[i] = {
31503 selector: selector,
31504 properties: []
31505 };
31506 return this; // chaining
31507}; // just store the property to be parsed later
31508
31509
31510sheetfn.css = function (name, value) {
31511 var i = this.length - 1;
31512
31513 if (string(name)) {
31514 this[i].properties.push({
31515 name: name,
31516 value: value
31517 });
31518 } else if (plainObject(name)) {
31519 var map = name;
31520 var propNames = Object.keys(map);
31521
31522 for (var j = 0; j < propNames.length; j++) {
31523 var key = propNames[j];
31524 var mapVal = map[key];
31525
31526 if (mapVal == null) {
31527 continue;
31528 }
31529
31530 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31531
31532 if (prop == null) {
31533 continue;
31534 }
31535
31536 var _name = prop.name;
31537 var _value = mapVal;
31538 this[i].properties.push({
31539 name: _name,
31540 value: _value
31541 });
31542 }
31543 }
31544
31545 return this; // chaining
31546};
31547
31548sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31549
31550sheetfn.generateStyle = function (cy) {
31551 var style = new Style(cy);
31552 return this.appendToStyle(style);
31553}; // append a dummy stylesheet object on a real style object
31554
31555
31556sheetfn.appendToStyle = function (style) {
31557 for (var i = 0; i < this.length; i++) {
31558 var context = this[i];
31559 var selector = context.selector;
31560 var props = context.properties;
31561 style.selector(selector); // apply selector
31562
31563 for (var j = 0; j < props.length; j++) {
31564 var prop = props[j];
31565 style.css(prop.name, prop.value); // apply property
31566 }
31567 }
31568
31569 return style;
31570};
31571
31572var version = "3.16.0";
31573
31574var cytoscape = function cytoscape(options) {
31575 // if no options specified, use default
31576 if (options === undefined) {
31577 options = {};
31578 } // create instance
31579
31580
31581 if (plainObject(options)) {
31582 return new Core(options);
31583 } // allow for registration of extensions
31584 else if (string(options)) {
31585 return extension.apply(extension, arguments);
31586 }
31587}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31588
31589
31590cytoscape.use = function (ext) {
31591 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31592
31593 args.unshift(cytoscape); // cytoscape is first arg to ext
31594
31595 ext.apply(null, args);
31596 return this;
31597};
31598
31599cytoscape.warnings = function (bool) {
31600 return warnings(bool);
31601}; // replaced by build system
31602
31603
31604cytoscape.version = version; // expose public apis (mostly for extensions)
31605
31606cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31607
31608export default cytoscape;