UNPKG

857 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2020, The Cytoscape Consortium.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the “Software”), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 * of the Software, and to permit persons to whom the Software is furnished to do
9 * so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
23'use strict';
24
25function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
26
27var util = _interopDefault(require('lodash.debounce'));
28var Heap = _interopDefault(require('heap'));
29
30function _typeof(obj) {
31 if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
32 _typeof = function (obj) {
33 return typeof obj;
34 };
35 } else {
36 _typeof = function (obj) {
37 return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
38 };
39 }
40
41 return _typeof(obj);
42}
43
44function _classCallCheck(instance, Constructor) {
45 if (!(instance instanceof Constructor)) {
46 throw new TypeError("Cannot call a class as a function");
47 }
48}
49
50function _defineProperties(target, props) {
51 for (var i = 0; i < props.length; i++) {
52 var descriptor = props[i];
53 descriptor.enumerable = descriptor.enumerable || false;
54 descriptor.configurable = true;
55 if ("value" in descriptor) descriptor.writable = true;
56 Object.defineProperty(target, descriptor.key, descriptor);
57 }
58}
59
60function _createClass(Constructor, protoProps, staticProps) {
61 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
62 if (staticProps) _defineProperties(Constructor, staticProps);
63 return Constructor;
64}
65
66function _defineProperty(obj, key, value) {
67 if (key in obj) {
68 Object.defineProperty(obj, key, {
69 value: value,
70 enumerable: true,
71 configurable: true,
72 writable: true
73 });
74 } else {
75 obj[key] = value;
76 }
77
78 return obj;
79}
80
81function _slicedToArray(arr, i) {
82 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest();
83}
84
85function _arrayWithHoles(arr) {
86 if (Array.isArray(arr)) return arr;
87}
88
89function _iterableToArrayLimit(arr, i) {
90 var _arr = [];
91 var _n = true;
92 var _d = false;
93 var _e = undefined;
94
95 try {
96 for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) {
97 _arr.push(_s.value);
98
99 if (i && _arr.length === i) break;
100 }
101 } catch (err) {
102 _d = true;
103 _e = err;
104 } finally {
105 try {
106 if (!_n && _i["return"] != null) _i["return"]();
107 } finally {
108 if (_d) throw _e;
109 }
110 }
111
112 return _arr;
113}
114
115function _nonIterableRest() {
116 throw new TypeError("Invalid attempt to destructure non-iterable instance");
117}
118
119var window$1 = typeof window === 'undefined' ? null : window; // eslint-disable-line no-undef
120
121var navigator = window$1 ? window$1.navigator : null;
122var document$1 = window$1 ? window$1.document : null;
123
124var typeofstr = _typeof('');
125
126var typeofobj = _typeof({});
127
128var typeoffn = _typeof(function () {});
129
130var typeofhtmlele = typeof HTMLElement === "undefined" ? "undefined" : _typeof(HTMLElement);
131
132var instanceStr = function instanceStr(obj) {
133 return obj && obj.instanceString && fn(obj.instanceString) ? obj.instanceString() : null;
134};
135
136var string = function string(obj) {
137 return obj != null && _typeof(obj) == typeofstr;
138};
139var fn = function fn(obj) {
140 return obj != null && _typeof(obj) === typeoffn;
141};
142var array = function array(obj) {
143 return Array.isArray ? Array.isArray(obj) : obj != null && obj instanceof Array;
144};
145var plainObject = function plainObject(obj) {
146 return obj != null && _typeof(obj) === typeofobj && !array(obj) && obj.constructor === Object;
147};
148var object = function object(obj) {
149 return obj != null && _typeof(obj) === typeofobj;
150};
151var number = function number(obj) {
152 return obj != null && _typeof(obj) === _typeof(1) && !isNaN(obj);
153};
154var integer = function integer(obj) {
155 return number(obj) && Math.floor(obj) === obj;
156};
157var htmlElement = function htmlElement(obj) {
158 if ('undefined' === typeofhtmlele) {
159 return undefined;
160 } else {
161 return null != obj && obj instanceof HTMLElement;
162 }
163};
164var elementOrCollection = function elementOrCollection(obj) {
165 return element(obj) || collection(obj);
166};
167var element = function element(obj) {
168 return instanceStr(obj) === 'collection' && obj._private.single;
169};
170var collection = function collection(obj) {
171 return instanceStr(obj) === 'collection' && !obj._private.single;
172};
173var core = function core(obj) {
174 return instanceStr(obj) === 'core';
175};
176var stylesheet = function stylesheet(obj) {
177 return instanceStr(obj) === 'stylesheet';
178};
179var event = function event(obj) {
180 return instanceStr(obj) === 'event';
181};
182var emptyString = function emptyString(obj) {
183 if (obj === undefined || obj === null) {
184 // null is empty
185 return true;
186 } else if (obj === '' || obj.match(/^\s+$/)) {
187 return true; // empty string is empty
188 }
189
190 return false; // otherwise, we don't know what we've got
191};
192var domElement = function domElement(obj) {
193 if (typeof HTMLElement === 'undefined') {
194 return false; // we're not in a browser so it doesn't matter
195 } else {
196 return obj instanceof HTMLElement;
197 }
198};
199var boundingBox = function boundingBox(obj) {
200 return plainObject(obj) && number(obj.x1) && number(obj.x2) && number(obj.y1) && number(obj.y2);
201};
202var promise = function promise(obj) {
203 return object(obj) && fn(obj.then);
204};
205var ms = function ms() {
206 return navigator && navigator.userAgent.match(/msie|trident|edge/i);
207}; // probably a better way to detect this...
208
209var memoize = function memoize(fn, keyFn) {
210 if (!keyFn) {
211 keyFn = function keyFn() {
212 if (arguments.length === 1) {
213 return arguments[0];
214 } else if (arguments.length === 0) {
215 return 'undefined';
216 }
217
218 var args = [];
219
220 for (var i = 0; i < arguments.length; i++) {
221 args.push(arguments[i]);
222 }
223
224 return args.join('$');
225 };
226 }
227
228 var memoizedFn = function memoizedFn() {
229 var self = this;
230 var args = arguments;
231 var ret;
232 var k = keyFn.apply(self, args);
233 var cache = memoizedFn.cache;
234
235 if (!(ret = cache[k])) {
236 ret = cache[k] = fn.apply(self, args);
237 }
238
239 return ret;
240 };
241
242 memoizedFn.cache = {};
243 return memoizedFn;
244};
245
246var camel2dash = memoize(function (str) {
247 return str.replace(/([A-Z])/g, function (v) {
248 return '-' + v.toLowerCase();
249 });
250});
251var dash2camel = memoize(function (str) {
252 return str.replace(/(-\w)/g, function (v) {
253 return v[1].toUpperCase();
254 });
255});
256var prependCamel = memoize(function (prefix, str) {
257 return prefix + str[0].toUpperCase() + str.substring(1);
258}, function (prefix, str) {
259 return prefix + '$' + str;
260});
261var capitalize = function capitalize(str) {
262 if (emptyString(str)) {
263 return str;
264 }
265
266 return str.charAt(0).toUpperCase() + str.substring(1);
267};
268
269var number$1 = '(?:[-+]?(?:(?:\\d+|\\d*\\.\\d+)(?:[Ee][+-]?\\d+)?))';
270var rgba = 'rgb[a]?\\((' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)\\s*,\\s*(' + number$1 + '[%]?)(?:\\s*,\\s*(' + number$1 + '))?\\)';
271var rgbaNoBackRefs = 'rgb[a]?\\((?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)\\s*,\\s*(?:' + number$1 + '[%]?)(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
272var hsla = 'hsl[a]?\\((' + number$1 + ')\\s*,\\s*(' + number$1 + '[%])\\s*,\\s*(' + number$1 + '[%])(?:\\s*,\\s*(' + number$1 + '))?\\)';
273var hslaNoBackRefs = 'hsl[a]?\\((?:' + number$1 + ')\\s*,\\s*(?:' + number$1 + '[%])\\s*,\\s*(?:' + number$1 + '[%])(?:\\s*,\\s*(?:' + number$1 + '))?\\)';
274var hex3 = '\\#[0-9a-fA-F]{3}';
275var hex6 = '\\#[0-9a-fA-F]{6}';
276
277var ascending = function ascending(a, b) {
278 if (a < b) {
279 return -1;
280 } else if (a > b) {
281 return 1;
282 } else {
283 return 0;
284 }
285};
286var descending = function descending(a, b) {
287 return -1 * ascending(a, b);
288};
289
290var extend = Object.assign != null ? Object.assign.bind(Object) : function (tgt) {
291 var args = arguments;
292
293 for (var i = 1; i < args.length; i++) {
294 var obj = args[i];
295
296 if (obj == null) {
297 continue;
298 }
299
300 var keys = Object.keys(obj);
301
302 for (var j = 0; j < keys.length; j++) {
303 var k = keys[j];
304 tgt[k] = obj[k];
305 }
306 }
307
308 return tgt;
309};
310
311var hex2tuple = function hex2tuple(hex) {
312 if (!(hex.length === 4 || hex.length === 7) || hex[0] !== '#') {
313 return;
314 }
315
316 var shortHex = hex.length === 4;
317 var r, g, b;
318 var base = 16;
319
320 if (shortHex) {
321 r = parseInt(hex[1] + hex[1], base);
322 g = parseInt(hex[2] + hex[2], base);
323 b = parseInt(hex[3] + hex[3], base);
324 } else {
325 r = parseInt(hex[1] + hex[2], base);
326 g = parseInt(hex[3] + hex[4], base);
327 b = parseInt(hex[5] + hex[6], base);
328 }
329
330 return [r, g, b];
331}; // get [r, g, b, a] from hsl(0, 0, 0) or hsla(0, 0, 0, 0)
332
333var hsl2tuple = function hsl2tuple(hsl) {
334 var ret;
335 var h, s, l, a, r, g, b;
336
337 function hue2rgb(p, q, t) {
338 if (t < 0) t += 1;
339 if (t > 1) t -= 1;
340 if (t < 1 / 6) return p + (q - p) * 6 * t;
341 if (t < 1 / 2) return q;
342 if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
343 return p;
344 }
345
346 var m = new RegExp('^' + hsla + '$').exec(hsl);
347
348 if (m) {
349 // get hue
350 h = parseInt(m[1]);
351
352 if (h < 0) {
353 h = (360 - -1 * h % 360) % 360;
354 } else if (h > 360) {
355 h = h % 360;
356 }
357
358 h /= 360; // normalise on [0, 1]
359
360 s = parseFloat(m[2]);
361
362 if (s < 0 || s > 100) {
363 return;
364 } // saturation is [0, 100]
365
366
367 s = s / 100; // normalise on [0, 1]
368
369 l = parseFloat(m[3]);
370
371 if (l < 0 || l > 100) {
372 return;
373 } // lightness is [0, 100]
374
375
376 l = l / 100; // normalise on [0, 1]
377
378 a = m[4];
379
380 if (a !== undefined) {
381 a = parseFloat(a);
382
383 if (a < 0 || a > 1) {
384 return;
385 } // alpha is [0, 1]
386
387 } // now, convert to rgb
388 // code from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
389
390
391 if (s === 0) {
392 r = g = b = Math.round(l * 255); // achromatic
393 } else {
394 var q = l < 0.5 ? l * (1 + s) : l + s - l * s;
395 var p = 2 * l - q;
396 r = Math.round(255 * hue2rgb(p, q, h + 1 / 3));
397 g = Math.round(255 * hue2rgb(p, q, h));
398 b = Math.round(255 * hue2rgb(p, q, h - 1 / 3));
399 }
400
401 ret = [r, g, b, a];
402 }
403
404 return ret;
405}; // get [r, g, b, a] from rgb(0, 0, 0) or rgba(0, 0, 0, 0)
406
407var rgb2tuple = function rgb2tuple(rgb) {
408 var ret;
409 var m = new RegExp('^' + rgba + '$').exec(rgb);
410
411 if (m) {
412 ret = [];
413 var isPct = [];
414
415 for (var i = 1; i <= 3; i++) {
416 var channel = m[i];
417
418 if (channel[channel.length - 1] === '%') {
419 isPct[i] = true;
420 }
421
422 channel = parseFloat(channel);
423
424 if (isPct[i]) {
425 channel = channel / 100 * 255; // normalise to [0, 255]
426 }
427
428 if (channel < 0 || channel > 255) {
429 return;
430 } // invalid channel value
431
432
433 ret.push(Math.floor(channel));
434 }
435
436 var atLeastOneIsPct = isPct[1] || isPct[2] || isPct[3];
437 var allArePct = isPct[1] && isPct[2] && isPct[3];
438
439 if (atLeastOneIsPct && !allArePct) {
440 return;
441 } // must all be percent values if one is
442
443
444 var alpha = m[4];
445
446 if (alpha !== undefined) {
447 alpha = parseFloat(alpha);
448
449 if (alpha < 0 || alpha > 1) {
450 return;
451 } // invalid alpha value
452
453
454 ret.push(alpha);
455 }
456 }
457
458 return ret;
459};
460var colorname2tuple = function colorname2tuple(color) {
461 return colors[color.toLowerCase()];
462};
463var color2tuple = function color2tuple(color) {
464 return (array(color) ? color : null) || colorname2tuple(color) || hex2tuple(color) || rgb2tuple(color) || hsl2tuple(color);
465};
466var colors = {
467 // special colour names
468 transparent: [0, 0, 0, 0],
469 // NB alpha === 0
470 // regular colours
471 aliceblue: [240, 248, 255],
472 antiquewhite: [250, 235, 215],
473 aqua: [0, 255, 255],
474 aquamarine: [127, 255, 212],
475 azure: [240, 255, 255],
476 beige: [245, 245, 220],
477 bisque: [255, 228, 196],
478 black: [0, 0, 0],
479 blanchedalmond: [255, 235, 205],
480 blue: [0, 0, 255],
481 blueviolet: [138, 43, 226],
482 brown: [165, 42, 42],
483 burlywood: [222, 184, 135],
484 cadetblue: [95, 158, 160],
485 chartreuse: [127, 255, 0],
486 chocolate: [210, 105, 30],
487 coral: [255, 127, 80],
488 cornflowerblue: [100, 149, 237],
489 cornsilk: [255, 248, 220],
490 crimson: [220, 20, 60],
491 cyan: [0, 255, 255],
492 darkblue: [0, 0, 139],
493 darkcyan: [0, 139, 139],
494 darkgoldenrod: [184, 134, 11],
495 darkgray: [169, 169, 169],
496 darkgreen: [0, 100, 0],
497 darkgrey: [169, 169, 169],
498 darkkhaki: [189, 183, 107],
499 darkmagenta: [139, 0, 139],
500 darkolivegreen: [85, 107, 47],
501 darkorange: [255, 140, 0],
502 darkorchid: [153, 50, 204],
503 darkred: [139, 0, 0],
504 darksalmon: [233, 150, 122],
505 darkseagreen: [143, 188, 143],
506 darkslateblue: [72, 61, 139],
507 darkslategray: [47, 79, 79],
508 darkslategrey: [47, 79, 79],
509 darkturquoise: [0, 206, 209],
510 darkviolet: [148, 0, 211],
511 deeppink: [255, 20, 147],
512 deepskyblue: [0, 191, 255],
513 dimgray: [105, 105, 105],
514 dimgrey: [105, 105, 105],
515 dodgerblue: [30, 144, 255],
516 firebrick: [178, 34, 34],
517 floralwhite: [255, 250, 240],
518 forestgreen: [34, 139, 34],
519 fuchsia: [255, 0, 255],
520 gainsboro: [220, 220, 220],
521 ghostwhite: [248, 248, 255],
522 gold: [255, 215, 0],
523 goldenrod: [218, 165, 32],
524 gray: [128, 128, 128],
525 grey: [128, 128, 128],
526 green: [0, 128, 0],
527 greenyellow: [173, 255, 47],
528 honeydew: [240, 255, 240],
529 hotpink: [255, 105, 180],
530 indianred: [205, 92, 92],
531 indigo: [75, 0, 130],
532 ivory: [255, 255, 240],
533 khaki: [240, 230, 140],
534 lavender: [230, 230, 250],
535 lavenderblush: [255, 240, 245],
536 lawngreen: [124, 252, 0],
537 lemonchiffon: [255, 250, 205],
538 lightblue: [173, 216, 230],
539 lightcoral: [240, 128, 128],
540 lightcyan: [224, 255, 255],
541 lightgoldenrodyellow: [250, 250, 210],
542 lightgray: [211, 211, 211],
543 lightgreen: [144, 238, 144],
544 lightgrey: [211, 211, 211],
545 lightpink: [255, 182, 193],
546 lightsalmon: [255, 160, 122],
547 lightseagreen: [32, 178, 170],
548 lightskyblue: [135, 206, 250],
549 lightslategray: [119, 136, 153],
550 lightslategrey: [119, 136, 153],
551 lightsteelblue: [176, 196, 222],
552 lightyellow: [255, 255, 224],
553 lime: [0, 255, 0],
554 limegreen: [50, 205, 50],
555 linen: [250, 240, 230],
556 magenta: [255, 0, 255],
557 maroon: [128, 0, 0],
558 mediumaquamarine: [102, 205, 170],
559 mediumblue: [0, 0, 205],
560 mediumorchid: [186, 85, 211],
561 mediumpurple: [147, 112, 219],
562 mediumseagreen: [60, 179, 113],
563 mediumslateblue: [123, 104, 238],
564 mediumspringgreen: [0, 250, 154],
565 mediumturquoise: [72, 209, 204],
566 mediumvioletred: [199, 21, 133],
567 midnightblue: [25, 25, 112],
568 mintcream: [245, 255, 250],
569 mistyrose: [255, 228, 225],
570 moccasin: [255, 228, 181],
571 navajowhite: [255, 222, 173],
572 navy: [0, 0, 128],
573 oldlace: [253, 245, 230],
574 olive: [128, 128, 0],
575 olivedrab: [107, 142, 35],
576 orange: [255, 165, 0],
577 orangered: [255, 69, 0],
578 orchid: [218, 112, 214],
579 palegoldenrod: [238, 232, 170],
580 palegreen: [152, 251, 152],
581 paleturquoise: [175, 238, 238],
582 palevioletred: [219, 112, 147],
583 papayawhip: [255, 239, 213],
584 peachpuff: [255, 218, 185],
585 peru: [205, 133, 63],
586 pink: [255, 192, 203],
587 plum: [221, 160, 221],
588 powderblue: [176, 224, 230],
589 purple: [128, 0, 128],
590 red: [255, 0, 0],
591 rosybrown: [188, 143, 143],
592 royalblue: [65, 105, 225],
593 saddlebrown: [139, 69, 19],
594 salmon: [250, 128, 114],
595 sandybrown: [244, 164, 96],
596 seagreen: [46, 139, 87],
597 seashell: [255, 245, 238],
598 sienna: [160, 82, 45],
599 silver: [192, 192, 192],
600 skyblue: [135, 206, 235],
601 slateblue: [106, 90, 205],
602 slategray: [112, 128, 144],
603 slategrey: [112, 128, 144],
604 snow: [255, 250, 250],
605 springgreen: [0, 255, 127],
606 steelblue: [70, 130, 180],
607 tan: [210, 180, 140],
608 teal: [0, 128, 128],
609 thistle: [216, 191, 216],
610 tomato: [255, 99, 71],
611 turquoise: [64, 224, 208],
612 violet: [238, 130, 238],
613 wheat: [245, 222, 179],
614 white: [255, 255, 255],
615 whitesmoke: [245, 245, 245],
616 yellow: [255, 255, 0],
617 yellowgreen: [154, 205, 50]
618};
619
620var setMap = function setMap(options) {
621 var obj = options.map;
622 var keys = options.keys;
623 var l = keys.length;
624
625 for (var i = 0; i < l; i++) {
626 var key = keys[i];
627
628 if (plainObject(key)) {
629 throw Error('Tried to set map with object key');
630 }
631
632 if (i < keys.length - 1) {
633 // extend the map if necessary
634 if (obj[key] == null) {
635 obj[key] = {};
636 }
637
638 obj = obj[key];
639 } else {
640 // set the value
641 obj[key] = options.value;
642 }
643 }
644}; // gets the value in a map even if it's not built in places
645
646var getMap = function getMap(options) {
647 var obj = options.map;
648 var keys = options.keys;
649 var l = keys.length;
650
651 for (var i = 0; i < l; i++) {
652 var key = keys[i];
653
654 if (plainObject(key)) {
655 throw Error('Tried to get map with object key');
656 }
657
658 obj = obj[key];
659
660 if (obj == null) {
661 return obj;
662 }
663 }
664
665 return obj;
666}; // deletes the entry in the map
667
668var performance = window$1 ? window$1.performance : null;
669var pnow = performance && performance.now ? function () {
670 return performance.now();
671} : function () {
672 return Date.now();
673};
674
675var raf = function () {
676 if (window$1) {
677 if (window$1.requestAnimationFrame) {
678 return function (fn) {
679 window$1.requestAnimationFrame(fn);
680 };
681 } else if (window$1.mozRequestAnimationFrame) {
682 return function (fn) {
683 window$1.mozRequestAnimationFrame(fn);
684 };
685 } else if (window$1.webkitRequestAnimationFrame) {
686 return function (fn) {
687 window$1.webkitRequestAnimationFrame(fn);
688 };
689 } else if (window$1.msRequestAnimationFrame) {
690 return function (fn) {
691 window$1.msRequestAnimationFrame(fn);
692 };
693 }
694 }
695
696 return function (fn) {
697 if (fn) {
698 setTimeout(function () {
699 fn(pnow());
700 }, 1000 / 60);
701 }
702 };
703}();
704
705var requestAnimationFrame = function requestAnimationFrame(fn) {
706 return raf(fn);
707};
708var performanceNow = pnow;
709
710var DEFAULT_SEED = 5381;
711var hashIterableInts = function hashIterableInts(iterator) {
712 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
713 // djb2/string-hash
714 var hash = seed;
715 var entry;
716
717 for (;;) {
718 entry = iterator.next();
719
720 if (entry.done) {
721 break;
722 }
723
724 hash = (hash << 5) + hash + entry.value | 0;
725 }
726
727 return hash;
728};
729var hashInt = function hashInt(num) {
730 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_SEED;
731 // djb2/string-hash
732 return (seed << 5) + seed + num | 0;
733};
734var hashIntsArray = function hashIntsArray(ints, seed) {
735 var entry = {
736 value: 0,
737 done: false
738 };
739 var i = 0;
740 var length = ints.length;
741 var iterator = {
742 next: function next() {
743 if (i < length) {
744 entry.value = ints[i++];
745 } else {
746 entry.done = true;
747 }
748
749 return entry;
750 }
751 };
752 return hashIterableInts(iterator, seed);
753};
754var hashString = function hashString(str, seed) {
755 var entry = {
756 value: 0,
757 done: false
758 };
759 var i = 0;
760 var length = str.length;
761 var iterator = {
762 next: function next() {
763 if (i < length) {
764 entry.value = str.charCodeAt(i++);
765 } else {
766 entry.done = true;
767 }
768
769 return entry;
770 }
771 };
772 return hashIterableInts(iterator, seed);
773};
774var hashStrings = function hashStrings() {
775 return hashStringsArray(arguments);
776};
777var hashStringsArray = function hashStringsArray(strs) {
778 var hash;
779
780 for (var i = 0; i < strs.length; i++) {
781 var str = strs[i];
782
783 if (i === 0) {
784 hash = hashString(str);
785 } else {
786 hash = hashString(str, hash);
787 }
788 }
789
790 return hash;
791};
792
793/*global console */
794var warningsEnabled = true;
795var warnSupported = console.warn != null; // eslint-disable-line no-console
796
797var traceSupported = console.trace != null; // eslint-disable-line no-console
798
799var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
800var trueify = function trueify() {
801 return true;
802};
803var falsify = function falsify() {
804 return false;
805};
806var zeroify = function zeroify() {
807 return 0;
808};
809var noop = function noop() {};
810var error = function error(msg) {
811 throw new Error(msg);
812};
813var warnings = function warnings(enabled) {
814 if (enabled !== undefined) {
815 warningsEnabled = !!enabled;
816 } else {
817 return warningsEnabled;
818 }
819};
820var warn = function warn(msg) {
821 /* eslint-disable no-console */
822 if (!warnings()) {
823 return;
824 }
825
826 if (warnSupported) {
827 console.warn(msg);
828 } else {
829 console.log(msg);
830
831 if (traceSupported) {
832 console.trace();
833 }
834 }
835};
836/* eslint-enable */
837
838var clone = function clone(obj) {
839 return extend({}, obj);
840}; // gets a shallow copy of the argument
841
842var copy = function copy(obj) {
843 if (obj == null) {
844 return obj;
845 }
846
847 if (array(obj)) {
848 return obj.slice();
849 } else if (plainObject(obj)) {
850 return clone(obj);
851 } else {
852 return obj;
853 }
854};
855var copyArray = function copyArray(arr) {
856 return arr.slice();
857};
858var uuid = function uuid(a, b
859/* placeholders */
860) {
861 for ( // loop :)
862 b = a = ''; // b - result , a - numeric letiable
863 a++ < 36; //
864 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
865 ? // return a random number or 4
866 (a ^ 15 // if "a" is not 15
867 ? // genetate a random number from 0 to 15
868 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
869 : 4 // otherwise 4
870 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
871 ) {
872 }
873
874 return b;
875};
876var _staticEmptyObject = {};
877var staticEmptyObject = function staticEmptyObject() {
878 return _staticEmptyObject;
879};
880var defaults = function defaults(_defaults) {
881 var keys = Object.keys(_defaults);
882 return function (opts) {
883 var filledOpts = {};
884
885 for (var i = 0; i < keys.length; i++) {
886 var key = keys[i];
887 var optVal = opts == null ? undefined : opts[key];
888 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
889 }
890
891 return filledOpts;
892 };
893};
894var removeFromArray = function removeFromArray(arr, ele, manyCopies) {
895 for (var i = arr.length; i >= 0; i--) {
896 if (arr[i] === ele) {
897 arr.splice(i, 1);
898
899 if (!manyCopies) {
900 break;
901 }
902 }
903 }
904};
905var clearArray = function clearArray(arr) {
906 arr.splice(0, arr.length);
907};
908var push = function push(arr, otherArr) {
909 for (var i = 0; i < otherArr.length; i++) {
910 var el = otherArr[i];
911 arr.push(el);
912 }
913};
914var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
915 if (prefix) {
916 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
917 }
918
919 return obj[propName];
920};
921var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
922 if (prefix) {
923 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
924 }
925
926 obj[propName] = value;
927};
928
929/* global Map */
930var ObjectMap =
931/*#__PURE__*/
932function () {
933 function ObjectMap() {
934 _classCallCheck(this, ObjectMap);
935
936 this._obj = {};
937 }
938
939 _createClass(ObjectMap, [{
940 key: "set",
941 value: function set(key, val) {
942 this._obj[key] = val;
943 return this;
944 }
945 }, {
946 key: "delete",
947 value: function _delete(key) {
948 this._obj[key] = undefined;
949 return this;
950 }
951 }, {
952 key: "clear",
953 value: function clear() {
954 this._obj = {};
955 }
956 }, {
957 key: "has",
958 value: function has(key) {
959 return this._obj[key] !== undefined;
960 }
961 }, {
962 key: "get",
963 value: function get(key) {
964 return this._obj[key];
965 }
966 }]);
967
968 return ObjectMap;
969}();
970
971var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
972
973/* global Set */
974var undef = "undefined" ;
975
976var ObjectSet =
977/*#__PURE__*/
978function () {
979 function ObjectSet(arrayOrObjectSet) {
980 _classCallCheck(this, ObjectSet);
981
982 this._obj = Object.create(null);
983 this.size = 0;
984
985 if (arrayOrObjectSet != null) {
986 var arr;
987
988 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
989 arr = arrayOrObjectSet.toArray();
990 } else {
991 arr = arrayOrObjectSet;
992 }
993
994 for (var i = 0; i < arr.length; i++) {
995 this.add(arr[i]);
996 }
997 }
998 }
999
1000 _createClass(ObjectSet, [{
1001 key: "instanceString",
1002 value: function instanceString() {
1003 return 'set';
1004 }
1005 }, {
1006 key: "add",
1007 value: function add(val) {
1008 var o = this._obj;
1009
1010 if (o[val] !== 1) {
1011 o[val] = 1;
1012 this.size++;
1013 }
1014 }
1015 }, {
1016 key: "delete",
1017 value: function _delete(val) {
1018 var o = this._obj;
1019
1020 if (o[val] === 1) {
1021 o[val] = 0;
1022 this.size--;
1023 }
1024 }
1025 }, {
1026 key: "clear",
1027 value: function clear() {
1028 this._obj = Object.create(null);
1029 }
1030 }, {
1031 key: "has",
1032 value: function has(val) {
1033 return this._obj[val] === 1;
1034 }
1035 }, {
1036 key: "toArray",
1037 value: function toArray() {
1038 var _this = this;
1039
1040 return Object.keys(this._obj).filter(function (key) {
1041 return _this.has(key);
1042 });
1043 }
1044 }, {
1045 key: "forEach",
1046 value: function forEach(callback, thisArg) {
1047 return this.toArray().forEach(callback, thisArg);
1048 }
1049 }]);
1050
1051 return ObjectSet;
1052}();
1053
1054var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1055
1056var Element = function Element(cy, params, restore) {
1057 restore = restore === undefined || restore ? true : false;
1058
1059 if (cy === undefined || params === undefined || !core(cy)) {
1060 error('An element must have a core reference and parameters set');
1061 return;
1062 }
1063
1064 var group = params.group; // try to automatically infer the group if unspecified
1065
1066 if (group == null) {
1067 if (params.data && params.data.source != null && params.data.target != null) {
1068 group = 'edges';
1069 } else {
1070 group = 'nodes';
1071 }
1072 } // validate group
1073
1074
1075 if (group !== 'nodes' && group !== 'edges') {
1076 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1077 return;
1078 } // make the element array-like, just like a collection
1079
1080
1081 this.length = 1;
1082 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1083
1084 var _p = this._private = {
1085 cy: cy,
1086 single: true,
1087 // indicates this is an element
1088 data: params.data || {},
1089 // data object
1090 position: params.position || {
1091 x: 0,
1092 y: 0
1093 },
1094 // (x, y) position pair
1095 autoWidth: undefined,
1096 // width and height of nodes calculated by the renderer when set to special 'auto' value
1097 autoHeight: undefined,
1098 autoPadding: undefined,
1099 compoundBoundsClean: false,
1100 // whether the compound dimensions need to be recalculated the next time dimensions are read
1101 listeners: [],
1102 // array of bound listeners
1103 group: group,
1104 // string; 'nodes' or 'edges'
1105 style: {},
1106 // properties as set by the style
1107 rstyle: {},
1108 // properties for style sent from the renderer to the core
1109 styleCxts: [],
1110 // applied style contexts from the styler
1111 styleKeys: {},
1112 // per-group keys of style property values
1113 removed: true,
1114 // whether it's inside the vis; true if removed (set true here since we call restore)
1115 selected: params.selected ? true : false,
1116 // whether it's selected
1117 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1118 // whether it's selectable
1119 locked: params.locked ? true : false,
1120 // whether the element is locked (cannot be moved)
1121 grabbed: false,
1122 // whether the element is grabbed by the mouse; renderer sets this privately
1123 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1124 // whether the element can be grabbed
1125 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1126 // whether the element has passthrough panning enabled
1127 active: false,
1128 // whether the element is active from user interaction
1129 classes: new Set$1(),
1130 // map ( className => true )
1131 animation: {
1132 // object for currently-running animations
1133 current: [],
1134 queue: []
1135 },
1136 rscratch: {},
1137 // object in which the renderer can store information
1138 scratch: params.scratch || {},
1139 // scratch objects
1140 edges: [],
1141 // array of connected edges
1142 children: [],
1143 // array of children
1144 parent: null,
1145 // parent ref
1146 traversalCache: {},
1147 // cache of output of traversal functions
1148 backgrounding: false,
1149 // whether background images are loading
1150 bbCache: null,
1151 // cache of the current bounding box
1152 bbCacheShift: {
1153 x: 0,
1154 y: 0
1155 },
1156 // shift applied to cached bb to be applied on next get
1157 bodyBounds: null,
1158 // bounds cache of element body, w/o overlay
1159 overlayBounds: null,
1160 // bounds cache of element body, including overlay
1161 labelBounds: {
1162 // bounds cache of labels
1163 all: null,
1164 source: null,
1165 target: null,
1166 main: null
1167 },
1168 arrowBounds: {
1169 // bounds cache of edge arrows
1170 source: null,
1171 target: null,
1172 'mid-source': null,
1173 'mid-target': null
1174 }
1175 };
1176
1177 if (_p.position.x == null) {
1178 _p.position.x = 0;
1179 }
1180
1181 if (_p.position.y == null) {
1182 _p.position.y = 0;
1183 } // renderedPosition overrides if specified
1184
1185
1186 if (params.renderedPosition) {
1187 var rpos = params.renderedPosition;
1188 var pan = cy.pan();
1189 var zoom = cy.zoom();
1190 _p.position = {
1191 x: (rpos.x - pan.x) / zoom,
1192 y: (rpos.y - pan.y) / zoom
1193 };
1194 }
1195
1196 var classes = [];
1197
1198 if (array(params.classes)) {
1199 classes = params.classes;
1200 } else if (string(params.classes)) {
1201 classes = params.classes.split(/\s+/);
1202 }
1203
1204 for (var i = 0, l = classes.length; i < l; i++) {
1205 var cls = classes[i];
1206
1207 if (!cls || cls === '') {
1208 continue;
1209 }
1210
1211 _p.classes.add(cls);
1212 }
1213
1214 this.createEmitter();
1215 var bypass = params.style || params.css;
1216
1217 if (bypass) {
1218 warn('Setting a `style` bypass at element creation is deprecated');
1219 this.style(bypass);
1220 }
1221
1222 if (restore === undefined || restore) {
1223 this.restore();
1224 }
1225};
1226
1227var defineSearch = function defineSearch(params) {
1228 params = {
1229 bfs: params.bfs || !params.dfs,
1230 dfs: params.dfs || !params.bfs
1231 }; // from pseudocode on wikipedia
1232
1233 return function searchFn(roots, fn$1, directed) {
1234 var options;
1235
1236 if (plainObject(roots) && !elementOrCollection(roots)) {
1237 options = roots;
1238 roots = options.roots || options.root;
1239 fn$1 = options.visit;
1240 directed = options.directed;
1241 }
1242
1243 directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
1244 fn$1 = fn(fn$1) ? fn$1 : function () {};
1245 var cy = this._private.cy;
1246 var v = roots = string(roots) ? this.filter(roots) : roots;
1247 var Q = [];
1248 var connectedNodes = [];
1249 var connectedBy = {};
1250 var id2depth = {};
1251 var V = {};
1252 var j = 0;
1253 var found;
1254
1255 var _this$byGroup = this.byGroup(),
1256 nodes = _this$byGroup.nodes,
1257 edges = _this$byGroup.edges; // enqueue v
1258
1259
1260 for (var i = 0; i < v.length; i++) {
1261 var vi = v[i];
1262 var viId = vi.id();
1263
1264 if (vi.isNode()) {
1265 Q.unshift(vi);
1266
1267 if (params.bfs) {
1268 V[viId] = true;
1269 connectedNodes.push(vi);
1270 }
1271
1272 id2depth[viId] = 0;
1273 }
1274 }
1275
1276 var _loop2 = function _loop2() {
1277 var v = params.bfs ? Q.shift() : Q.pop();
1278 var vId = v.id();
1279
1280 if (params.dfs) {
1281 if (V[vId]) {
1282 return "continue";
1283 }
1284
1285 V[vId] = true;
1286 connectedNodes.push(v);
1287 }
1288
1289 var depth = id2depth[vId];
1290 var prevEdge = connectedBy[vId];
1291 var src = prevEdge != null ? prevEdge.source() : null;
1292 var tgt = prevEdge != null ? prevEdge.target() : null;
1293 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1294 var ret = void 0;
1295 ret = fn$1(v, prevEdge, prevNode, j++, depth);
1296
1297 if (ret === true) {
1298 found = v;
1299 return "break";
1300 }
1301
1302 if (ret === false) {
1303 return "break";
1304 }
1305
1306 var vwEdges = v.connectedEdges().filter(function (e) {
1307 return (!directed || e.source().same(v)) && edges.has(e);
1308 });
1309
1310 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1311 var e = vwEdges[_i2];
1312 var w = e.connectedNodes().filter(function (n) {
1313 return !n.same(v) && nodes.has(n);
1314 });
1315 var wId = w.id();
1316
1317 if (w.length !== 0 && !V[wId]) {
1318 w = w[0];
1319 Q.push(w);
1320
1321 if (params.bfs) {
1322 V[wId] = true;
1323 connectedNodes.push(w);
1324 }
1325
1326 connectedBy[wId] = e;
1327 id2depth[wId] = id2depth[vId] + 1;
1328 }
1329 }
1330 };
1331
1332 _loop: while (Q.length !== 0) {
1333 var _ret = _loop2();
1334
1335 switch (_ret) {
1336 case "continue":
1337 continue;
1338
1339 case "break":
1340 break _loop;
1341 }
1342 }
1343
1344 var connectedEles = cy.collection();
1345
1346 for (var _i = 0; _i < connectedNodes.length; _i++) {
1347 var node = connectedNodes[_i];
1348 var edge = connectedBy[node.id()];
1349
1350 if (edge != null) {
1351 connectedEles.merge(edge);
1352 }
1353
1354 connectedEles.merge(node);
1355 }
1356
1357 return {
1358 path: cy.collection(connectedEles),
1359 found: cy.collection(found)
1360 };
1361 };
1362}; // search, spanning trees, etc
1363
1364
1365var elesfn = {
1366 breadthFirstSearch: defineSearch({
1367 bfs: true
1368 }),
1369 depthFirstSearch: defineSearch({
1370 dfs: true
1371 })
1372}; // nice, short mathemathical alias
1373
1374elesfn.bfs = elesfn.breadthFirstSearch;
1375elesfn.dfs = elesfn.depthFirstSearch;
1376
1377var dijkstraDefaults = defaults({
1378 root: null,
1379 weight: function weight(edge) {
1380 return 1;
1381 },
1382 directed: false
1383});
1384var elesfn$1 = {
1385 dijkstra: function dijkstra(options) {
1386 if (!plainObject(options)) {
1387 var args = arguments;
1388 options = {
1389 root: args[0],
1390 weight: args[1],
1391 directed: args[2]
1392 };
1393 }
1394
1395 var _dijkstraDefaults = dijkstraDefaults(options),
1396 root = _dijkstraDefaults.root,
1397 weight = _dijkstraDefaults.weight,
1398 directed = _dijkstraDefaults.directed;
1399
1400 var eles = this;
1401 var weightFn = weight;
1402 var source = string(root) ? this.filter(root)[0] : root[0];
1403 var dist = {};
1404 var prev = {};
1405 var knownDist = {};
1406
1407 var _this$byGroup = this.byGroup(),
1408 nodes = _this$byGroup.nodes,
1409 edges = _this$byGroup.edges;
1410
1411 edges.unmergeBy(function (ele) {
1412 return ele.isLoop();
1413 });
1414
1415 var getDist = function getDist(node) {
1416 return dist[node.id()];
1417 };
1418
1419 var setDist = function setDist(node, d) {
1420 dist[node.id()] = d;
1421 Q.updateItem(node);
1422 };
1423
1424 var Q = new Heap(function (a, b) {
1425 return getDist(a) - getDist(b);
1426 });
1427
1428 for (var i = 0; i < nodes.length; i++) {
1429 var node = nodes[i];
1430 dist[node.id()] = node.same(source) ? 0 : Infinity;
1431 Q.push(node);
1432 }
1433
1434 var distBetween = function distBetween(u, v) {
1435 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
1436 var smallestDistance = Infinity;
1437 var smallestEdge;
1438
1439 for (var _i = 0; _i < uvs.length; _i++) {
1440 var edge = uvs[_i];
1441
1442 var _weight = weightFn(edge);
1443
1444 if (_weight < smallestDistance || !smallestEdge) {
1445 smallestDistance = _weight;
1446 smallestEdge = edge;
1447 }
1448 }
1449
1450 return {
1451 edge: smallestEdge,
1452 dist: smallestDistance
1453 };
1454 };
1455
1456 while (Q.size() > 0) {
1457 var u = Q.pop();
1458 var smalletsDist = getDist(u);
1459 var uid = u.id();
1460 knownDist[uid] = smalletsDist;
1461
1462 if (smalletsDist === Infinity) {
1463 continue;
1464 }
1465
1466 var neighbors = u.neighborhood().intersect(nodes);
1467
1468 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
1469 var v = neighbors[_i2];
1470 var vid = v.id();
1471 var vDist = distBetween(u, v);
1472 var alt = smalletsDist + vDist.dist;
1473
1474 if (alt < getDist(v)) {
1475 setDist(v, alt);
1476 prev[vid] = {
1477 node: u,
1478 edge: vDist.edge
1479 };
1480 }
1481 } // for
1482
1483 } // while
1484
1485
1486 return {
1487 distanceTo: function distanceTo(node) {
1488 var target = string(node) ? nodes.filter(node)[0] : node[0];
1489 return knownDist[target.id()];
1490 },
1491 pathTo: function pathTo(node) {
1492 var target = string(node) ? nodes.filter(node)[0] : node[0];
1493 var S = [];
1494 var u = target;
1495 var uid = u.id();
1496
1497 if (target.length > 0) {
1498 S.unshift(target);
1499
1500 while (prev[uid]) {
1501 var p = prev[uid];
1502 S.unshift(p.edge);
1503 S.unshift(p.node);
1504 u = p.node;
1505 uid = u.id();
1506 }
1507 }
1508
1509 return eles.spawn(S);
1510 }
1511 };
1512 }
1513};
1514
1515var elesfn$2 = {
1516 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
1517 // implemented from pseudocode from wikipedia
1518 kruskal: function kruskal(weightFn) {
1519 weightFn = weightFn || function (edge) {
1520 return 1;
1521 };
1522
1523 var _this$byGroup = this.byGroup(),
1524 nodes = _this$byGroup.nodes,
1525 edges = _this$byGroup.edges;
1526
1527 var numNodes = nodes.length;
1528 var forest = new Array(numNodes);
1529 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
1530
1531 var findSetIndex = function findSetIndex(ele) {
1532 for (var i = 0; i < forest.length; i++) {
1533 var eles = forest[i];
1534
1535 if (eles.has(ele)) {
1536 return i;
1537 }
1538 }
1539 }; // start with one forest per node
1540
1541
1542 for (var i = 0; i < numNodes; i++) {
1543 forest[i] = this.spawn(nodes[i]);
1544 }
1545
1546 var S = edges.sort(function (a, b) {
1547 return weightFn(a) - weightFn(b);
1548 });
1549
1550 for (var _i = 0; _i < S.length; _i++) {
1551 var edge = S[_i];
1552 var u = edge.source()[0];
1553 var v = edge.target()[0];
1554 var setUIndex = findSetIndex(u);
1555 var setVIndex = findSetIndex(v);
1556 var setU = forest[setUIndex];
1557 var setV = forest[setVIndex];
1558
1559 if (setUIndex !== setVIndex) {
1560 A.merge(edge); // combine forests for u and v
1561
1562 setU.merge(setV);
1563 forest.splice(setVIndex, 1);
1564 }
1565 }
1566
1567 return A;
1568 }
1569};
1570
1571var aStarDefaults = defaults({
1572 root: null,
1573 goal: null,
1574 weight: function weight(edge) {
1575 return 1;
1576 },
1577 heuristic: function heuristic(edge) {
1578 return 0;
1579 },
1580 directed: false
1581});
1582var elesfn$3 = {
1583 // Implemented from pseudocode from wikipedia
1584 aStar: function aStar(options) {
1585 var cy = this.cy();
1586
1587 var _aStarDefaults = aStarDefaults(options),
1588 root = _aStarDefaults.root,
1589 goal = _aStarDefaults.goal,
1590 heuristic = _aStarDefaults.heuristic,
1591 directed = _aStarDefaults.directed,
1592 weight = _aStarDefaults.weight;
1593
1594 root = cy.collection(root)[0];
1595 goal = cy.collection(goal)[0];
1596 var sid = root.id();
1597 var tid = goal.id();
1598 var gScore = {};
1599 var fScore = {};
1600 var closedSetIds = {};
1601 var openSet = new Heap(function (a, b) {
1602 return fScore[a.id()] - fScore[b.id()];
1603 });
1604 var openSetIds = new Set$1();
1605 var cameFrom = {};
1606 var cameFromEdge = {};
1607
1608 var addToOpenSet = function addToOpenSet(ele, id) {
1609 openSet.push(ele);
1610 openSetIds.add(id);
1611 };
1612
1613 var cMin, cMinId;
1614
1615 var popFromOpenSet = function popFromOpenSet() {
1616 cMin = openSet.pop();
1617 cMinId = cMin.id();
1618 openSetIds["delete"](cMinId);
1619 };
1620
1621 var isInOpenSet = function isInOpenSet(id) {
1622 return openSetIds.has(id);
1623 };
1624
1625 addToOpenSet(root, sid);
1626 gScore[sid] = 0;
1627 fScore[sid] = heuristic(root); // Counter
1628
1629 var steps = 0; // Main loop
1630
1631 while (openSet.size() > 0) {
1632 popFromOpenSet();
1633 steps++; // If we've found our goal, then we are done
1634
1635 if (cMinId === tid) {
1636 var path = [];
1637 var pathNode = goal;
1638 var pathNodeId = tid;
1639 var pathEdge = cameFromEdge[pathNodeId];
1640
1641 for (;;) {
1642 path.unshift(pathNode);
1643
1644 if (pathEdge != null) {
1645 path.unshift(pathEdge);
1646 }
1647
1648 pathNode = cameFrom[pathNodeId];
1649
1650 if (pathNode == null) {
1651 break;
1652 }
1653
1654 pathNodeId = pathNode.id();
1655 pathEdge = cameFromEdge[pathNodeId];
1656 }
1657
1658 return {
1659 found: true,
1660 distance: gScore[cMinId],
1661 path: this.spawn(path),
1662 steps: steps
1663 };
1664 } // Add cMin to processed nodes
1665
1666
1667 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
1668 // Take into account if graph is directed or not
1669
1670 var vwEdges = cMin._private.edges;
1671
1672 for (var i = 0; i < vwEdges.length; i++) {
1673 var e = vwEdges[i]; // edge must be in set of calling eles
1674
1675 if (!this.hasElementWithId(e.id())) {
1676 continue;
1677 } // cMin must be the source of edge if directed
1678
1679
1680 if (directed && e.data('source') !== cMinId) {
1681 continue;
1682 }
1683
1684 var wSrc = e.source();
1685 var wTgt = e.target();
1686 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
1687 var wid = w.id(); // node must be in set of calling eles
1688
1689 if (!this.hasElementWithId(wid)) {
1690 continue;
1691 } // if node is in closedSet, ignore it
1692
1693
1694 if (closedSetIds[wid]) {
1695 continue;
1696 } // New tentative score for node w
1697
1698
1699 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
1700 // w not present in openSet
1701 // OR
1702 // tentative gScore is less than previous value
1703 // w not in openSet
1704
1705 if (!isInOpenSet(wid)) {
1706 gScore[wid] = tempScore;
1707 fScore[wid] = tempScore + heuristic(w);
1708 addToOpenSet(w, wid);
1709 cameFrom[wid] = cMin;
1710 cameFromEdge[wid] = e;
1711 continue;
1712 } // w already in openSet, but with greater gScore
1713
1714
1715 if (tempScore < gScore[wid]) {
1716 gScore[wid] = tempScore;
1717 fScore[wid] = tempScore + heuristic(w);
1718 cameFrom[wid] = cMin;
1719 }
1720 } // End of neighbors update
1721
1722 } // End of main loop
1723 // If we've reached here, then we've not reached our goal
1724
1725
1726 return {
1727 found: false,
1728 distance: undefined,
1729 path: undefined,
1730 steps: steps
1731 };
1732 }
1733}; // elesfn
1734
1735var floydWarshallDefaults = defaults({
1736 weight: function weight(edge) {
1737 return 1;
1738 },
1739 directed: false
1740});
1741var elesfn$4 = {
1742 // Implemented from pseudocode from wikipedia
1743 floydWarshall: function floydWarshall(options) {
1744 var cy = this.cy();
1745
1746 var _floydWarshallDefault = floydWarshallDefaults(options),
1747 weight = _floydWarshallDefault.weight,
1748 directed = _floydWarshallDefault.directed;
1749
1750 var weightFn = weight;
1751
1752 var _this$byGroup = this.byGroup(),
1753 nodes = _this$byGroup.nodes,
1754 edges = _this$byGroup.edges;
1755
1756 var N = nodes.length;
1757 var Nsq = N * N;
1758
1759 var indexOf = function indexOf(node) {
1760 return nodes.indexOf(node);
1761 };
1762
1763 var atIndex = function atIndex(i) {
1764 return nodes[i];
1765 }; // Initialize distance matrix
1766
1767
1768 var dist = new Array(Nsq);
1769
1770 for (var n = 0; n < Nsq; n++) {
1771 var j = n % N;
1772 var i = (n - j) / N;
1773
1774 if (i === j) {
1775 dist[n] = 0;
1776 } else {
1777 dist[n] = Infinity;
1778 }
1779 } // Initialize matrix used for path reconstruction
1780 // Initialize distance matrix
1781
1782
1783 var next = new Array(Nsq);
1784 var edgeNext = new Array(Nsq); // Process edges
1785
1786 for (var _i = 0; _i < edges.length; _i++) {
1787 var edge = edges[_i];
1788 var src = edge.source()[0];
1789 var tgt = edge.target()[0];
1790
1791 if (src === tgt) {
1792 continue;
1793 } // exclude loops
1794
1795
1796 var s = indexOf(src);
1797 var t = indexOf(tgt);
1798 var st = s * N + t; // source to target index
1799
1800 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
1801
1802
1803 if (dist[st] > _weight) {
1804 dist[st] = _weight;
1805 next[st] = t;
1806 edgeNext[st] = edge;
1807 } // If undirected graph, process 'reversed' edge
1808
1809
1810 if (!directed) {
1811 var ts = t * N + s; // target to source index
1812
1813 if (!directed && dist[ts] > _weight) {
1814 dist[ts] = _weight;
1815 next[ts] = s;
1816 edgeNext[ts] = edge;
1817 }
1818 }
1819 } // Main loop
1820
1821
1822 for (var k = 0; k < N; k++) {
1823 for (var _i2 = 0; _i2 < N; _i2++) {
1824 var ik = _i2 * N + k;
1825
1826 for (var _j = 0; _j < N; _j++) {
1827 var ij = _i2 * N + _j;
1828 var kj = k * N + _j;
1829
1830 if (dist[ik] + dist[kj] < dist[ij]) {
1831 dist[ij] = dist[ik] + dist[kj];
1832 next[ij] = next[ik];
1833 }
1834 }
1835 }
1836 }
1837
1838 var getArgEle = function getArgEle(ele) {
1839 return (string(ele) ? cy.filter(ele) : ele)[0];
1840 };
1841
1842 var indexOfArgEle = function indexOfArgEle(ele) {
1843 return indexOf(getArgEle(ele));
1844 };
1845
1846 var res = {
1847 distance: function distance(from, to) {
1848 var i = indexOfArgEle(from);
1849 var j = indexOfArgEle(to);
1850 return dist[i * N + j];
1851 },
1852 path: function path(from, to) {
1853 var i = indexOfArgEle(from);
1854 var j = indexOfArgEle(to);
1855 var fromNode = atIndex(i);
1856
1857 if (i === j) {
1858 return fromNode.collection();
1859 }
1860
1861 if (next[i * N + j] == null) {
1862 return cy.collection();
1863 }
1864
1865 var path = cy.collection();
1866 var prev = i;
1867 var edge;
1868 path.merge(fromNode);
1869
1870 while (i !== j) {
1871 prev = i;
1872 i = next[i * N + j];
1873 edge = edgeNext[prev * N + i];
1874 path.merge(edge);
1875 path.merge(atIndex(i));
1876 }
1877
1878 return path;
1879 }
1880 };
1881 return res;
1882 } // floydWarshall
1883
1884}; // elesfn
1885
1886var bellmanFordDefaults = defaults({
1887 weight: function weight(edge) {
1888 return 1;
1889 },
1890 directed: false,
1891 root: null
1892});
1893var elesfn$5 = {
1894 // Implemented from pseudocode from wikipedia
1895 bellmanFord: function bellmanFord(options) {
1896 var _this = this;
1897
1898 var _bellmanFordDefaults = bellmanFordDefaults(options),
1899 weight = _bellmanFordDefaults.weight,
1900 directed = _bellmanFordDefaults.directed,
1901 root = _bellmanFordDefaults.root;
1902
1903 var weightFn = weight;
1904 var eles = this;
1905 var cy = this.cy();
1906
1907 var _this$byGroup = this.byGroup(),
1908 edges = _this$byGroup.edges,
1909 nodes = _this$byGroup.nodes;
1910
1911 var numNodes = nodes.length;
1912 var infoMap = new Map$1();
1913 var hasNegativeWeightCycle = false;
1914 var negativeWeightCycles = [];
1915 root = cy.collection(root)[0]; // in case selector passed
1916
1917 edges.unmergeBy(function (edge) {
1918 return edge.isLoop();
1919 });
1920 var numEdges = edges.length;
1921
1922 var getInfo = function getInfo(node) {
1923 var obj = infoMap.get(node.id());
1924
1925 if (!obj) {
1926 obj = {};
1927 infoMap.set(node.id(), obj);
1928 }
1929
1930 return obj;
1931 };
1932
1933 var getNodeFromTo = function getNodeFromTo(to) {
1934 return (string(to) ? cy.$(to) : to)[0];
1935 };
1936
1937 var distanceTo = function distanceTo(to) {
1938 return getInfo(getNodeFromTo(to)).dist;
1939 };
1940
1941 var pathTo = function pathTo(to) {
1942 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
1943 var end = getNodeFromTo(to);
1944 var path = [];
1945 var node = end;
1946
1947 for (;;) {
1948 if (node == null) {
1949 return _this.spawn();
1950 }
1951
1952 var _getInfo = getInfo(node),
1953 edge = _getInfo.edge,
1954 pred = _getInfo.pred;
1955
1956 path.unshift(node[0]);
1957
1958 if (node.same(thisStart) && path.length > 0) {
1959 break;
1960 }
1961
1962 if (edge != null) {
1963 path.unshift(edge);
1964 }
1965
1966 node = pred;
1967 }
1968
1969 return eles.spawn(path);
1970 }; // Initializations { dist, pred, edge }
1971
1972
1973 for (var i = 0; i < numNodes; i++) {
1974 var node = nodes[i];
1975 var info = getInfo(node);
1976
1977 if (node.same(root)) {
1978 info.dist = 0;
1979 } else {
1980 info.dist = Infinity;
1981 }
1982
1983 info.pred = null;
1984 info.edge = null;
1985 } // Edges relaxation
1986
1987
1988 var replacedEdge = false;
1989
1990 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
1991 var dist = info1.dist + weight;
1992
1993 if (dist < info2.dist && !edge.same(info1.edge)) {
1994 info2.dist = dist;
1995 info2.pred = node1;
1996 info2.edge = edge;
1997 replacedEdge = true;
1998 }
1999 };
2000
2001 for (var _i = 1; _i < numNodes; _i++) {
2002 replacedEdge = false;
2003
2004 for (var e = 0; e < numEdges; e++) {
2005 var edge = edges[e];
2006 var src = edge.source();
2007 var tgt = edge.target();
2008
2009 var _weight = weightFn(edge);
2010
2011 var srcInfo = getInfo(src);
2012 var tgtInfo = getInfo(tgt);
2013 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2014
2015 if (!directed) {
2016 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2017 }
2018 }
2019
2020 if (!replacedEdge) {
2021 break;
2022 }
2023 }
2024
2025 if (replacedEdge) {
2026 // Check for negative weight cycles
2027 for (var _e = 0; _e < numEdges; _e++) {
2028 var _edge = edges[_e];
2029
2030 var _src = _edge.source();
2031
2032 var _tgt = _edge.target();
2033
2034 var _weight2 = weightFn(_edge);
2035
2036 var srcDist = getInfo(_src).dist;
2037 var tgtDist = getInfo(_tgt).dist;
2038
2039 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2040 warn('Graph contains a negative weight cycle for Bellman-Ford');
2041 hasNegativeWeightCycle = true;
2042 break;
2043 }
2044 }
2045 }
2046
2047 return {
2048 distanceTo: distanceTo,
2049 pathTo: pathTo,
2050 hasNegativeWeightCycle: hasNegativeWeightCycle,
2051 negativeWeightCycles: negativeWeightCycles
2052 };
2053 } // bellmanFord
2054
2055}; // elesfn
2056
2057var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2058// Updates the remaining edge lists
2059// Receives as a paramater the edge which causes the collapse
2060
2061var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2062 if (remainingEdges.length === 0) {
2063 error("Karger-Stein must be run on a connected (sub)graph");
2064 }
2065
2066 var edgeInfo = remainingEdges[edgeIndex];
2067 var sourceIn = edgeInfo[1];
2068 var targetIn = edgeInfo[2];
2069 var partition1 = nodeMap[sourceIn];
2070 var partition2 = nodeMap[targetIn];
2071 var newEdges = remainingEdges; // re-use array
2072 // Delete all edges between partition1 and partition2
2073
2074 for (var i = newEdges.length - 1; i >= 0; i--) {
2075 var edge = newEdges[i];
2076 var src = edge[1];
2077 var tgt = edge[2];
2078
2079 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2080 newEdges.splice(i, 1);
2081 }
2082 } // All edges pointing to partition2 should now point to partition1
2083
2084
2085 for (var _i = 0; _i < newEdges.length; _i++) {
2086 var _edge = newEdges[_i];
2087
2088 if (_edge[1] === partition2) {
2089 // Check source
2090 newEdges[_i] = _edge.slice(); // copy
2091
2092 newEdges[_i][1] = partition1;
2093 } else if (_edge[2] === partition2) {
2094 // Check target
2095 newEdges[_i] = _edge.slice(); // copy
2096
2097 newEdges[_i][2] = partition1;
2098 }
2099 } // Move all nodes from partition2 to partition1
2100
2101
2102 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2103 if (nodeMap[_i2] === partition2) {
2104 nodeMap[_i2] = partition1;
2105 }
2106 }
2107
2108 return newEdges;
2109}; // Contracts a graph until we reach a certain number of meta nodes
2110
2111
2112var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2113 while (size > sizeLimit) {
2114 // Choose an edge randomly
2115 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2116
2117 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2118 size--;
2119 }
2120
2121 return remainingEdges;
2122};
2123
2124var elesfn$6 = {
2125 // Computes the minimum cut of an undirected graph
2126 // Returns the correct answer with high probability
2127 kargerStein: function kargerStein() {
2128 var _this = this;
2129
2130 var _this$byGroup = this.byGroup(),
2131 nodes = _this$byGroup.nodes,
2132 edges = _this$byGroup.edges;
2133
2134 edges.unmergeBy(function (edge) {
2135 return edge.isLoop();
2136 });
2137 var numNodes = nodes.length;
2138 var numEdges = edges.length;
2139 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2140 var stopSize = Math.floor(numNodes / sqrt2);
2141
2142 if (numNodes < 2) {
2143 error('At least 2 nodes are required for Karger-Stein algorithm');
2144 return undefined;
2145 } // Now store edge destination as indexes
2146 // Format for each edge (edge index, source node index, target node index)
2147
2148
2149 var edgeIndexes = [];
2150
2151 for (var i = 0; i < numEdges; i++) {
2152 var e = edges[i];
2153 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2154 } // We will store the best cut found here
2155
2156
2157 var minCutSize = Infinity;
2158 var minCutEdgeIndexes = [];
2159 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2160
2161 var metaNodeMap = new Array(numNodes);
2162 var metaNodeMap2 = new Array(numNodes);
2163
2164 var copyNodesMap = function copyNodesMap(from, to) {
2165 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2166 to[_i3] = from[_i3];
2167 }
2168 }; // Main loop
2169
2170
2171 for (var iter = 0; iter <= numIter; iter++) {
2172 // Reset meta node partition
2173 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2174 metaNodeMap[_i4] = _i4;
2175 } // Contract until stop point (stopSize nodes)
2176
2177
2178 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2179 var edgesState2 = edgesState.slice(); // copy
2180 // Create a copy of the colapsed nodes state
2181
2182 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2183
2184 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2185 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2186
2187 if (res1.length <= res2.length && res1.length < minCutSize) {
2188 minCutSize = res1.length;
2189 minCutEdgeIndexes = res1;
2190 copyNodesMap(metaNodeMap, minCutNodeMap);
2191 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2192 minCutSize = res2.length;
2193 minCutEdgeIndexes = res2;
2194 copyNodesMap(metaNodeMap2, minCutNodeMap);
2195 }
2196 } // end of main loop
2197 // Construct result
2198
2199
2200 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2201 return edges[e[0]];
2202 }));
2203 var partition1 = this.spawn();
2204 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2205
2206 var witnessNodePartition = minCutNodeMap[0];
2207
2208 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2209 var partitionId = minCutNodeMap[_i5];
2210 var node = nodes[_i5];
2211
2212 if (partitionId === witnessNodePartition) {
2213 partition1.merge(node);
2214 } else {
2215 partition2.merge(node);
2216 }
2217 } // construct components corresponding to each disjoint subset of nodes
2218
2219
2220 var constructComponent = function constructComponent(subset) {
2221 var component = _this.spawn();
2222
2223 subset.forEach(function (node) {
2224 component.merge(node);
2225 node.connectedEdges().forEach(function (edge) {
2226 // ensure edge is within calling collection and edge is not in cut
2227 if (_this.contains(edge) && !cut.contains(edge)) {
2228 component.merge(edge);
2229 }
2230 });
2231 });
2232 return component;
2233 };
2234
2235 var components = [constructComponent(partition1), constructComponent(partition2)];
2236 var ret = {
2237 cut: cut,
2238 components: components,
2239 // n.b. partitions are included to be compatible with the old api spec
2240 // (could be removed in a future major version)
2241 partition1: partition1,
2242 partition2: partition2
2243 };
2244 return ret;
2245 }
2246}; // elesfn
2247
2248var copyPosition = function copyPosition(p) {
2249 return {
2250 x: p.x,
2251 y: p.y
2252 };
2253};
2254var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
2255 return {
2256 x: p.x * zoom + pan.x,
2257 y: p.y * zoom + pan.y
2258 };
2259};
2260var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
2261 return {
2262 x: (p.x - pan.x) / zoom,
2263 y: (p.y - pan.y) / zoom
2264 };
2265};
2266var array2point = function array2point(arr) {
2267 return {
2268 x: arr[0],
2269 y: arr[1]
2270 };
2271};
2272var min = function min(arr) {
2273 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2274 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2275 var min = Infinity;
2276
2277 for (var i = begin; i < end; i++) {
2278 var val = arr[i];
2279
2280 if (isFinite(val)) {
2281 min = Math.min(val, min);
2282 }
2283 }
2284
2285 return min;
2286};
2287var max = function max(arr) {
2288 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2289 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2290 var max = -Infinity;
2291
2292 for (var i = begin; i < end; i++) {
2293 var val = arr[i];
2294
2295 if (isFinite(val)) {
2296 max = Math.max(val, max);
2297 }
2298 }
2299
2300 return max;
2301};
2302var mean = function mean(arr) {
2303 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2304 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2305 var total = 0;
2306 var n = 0;
2307
2308 for (var i = begin; i < end; i++) {
2309 var val = arr[i];
2310
2311 if (isFinite(val)) {
2312 total += val;
2313 n++;
2314 }
2315 }
2316
2317 return total / n;
2318};
2319var median = function median(arr) {
2320 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2321 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2322 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
2323 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
2324 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
2325
2326 if (copy) {
2327 arr = arr.slice(begin, end);
2328 } else {
2329 if (end < arr.length) {
2330 arr.splice(end, arr.length - end);
2331 }
2332
2333 if (begin > 0) {
2334 arr.splice(0, begin);
2335 }
2336 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
2337
2338
2339 var off = 0; // offset from non-finite values
2340
2341 for (var i = arr.length - 1; i >= 0; i--) {
2342 var v = arr[i];
2343
2344 if (includeHoles) {
2345 if (!isFinite(v)) {
2346 arr[i] = -Infinity;
2347 off++;
2348 }
2349 } else {
2350 // just remove it if we don't want to consider holes
2351 arr.splice(i, 1);
2352 }
2353 }
2354
2355 if (sort) {
2356 arr.sort(function (a, b) {
2357 return a - b;
2358 }); // requires copy = true if you don't want to change the orig
2359 }
2360
2361 var len = arr.length;
2362 var mid = Math.floor(len / 2);
2363
2364 if (len % 2 !== 0) {
2365 return arr[mid + 1 + off];
2366 } else {
2367 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
2368 }
2369};
2370var deg2rad = function deg2rad(deg) {
2371 return Math.PI * deg / 180;
2372};
2373var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
2374 return Math.atan2(dispY, dispX) - Math.PI / 2;
2375};
2376var log2 = Math.log2 || function (n) {
2377 return Math.log(n) / Math.log(2);
2378};
2379var signum = function signum(x) {
2380 if (x > 0) {
2381 return 1;
2382 } else if (x < 0) {
2383 return -1;
2384 } else {
2385 return 0;
2386 }
2387};
2388var dist = function dist(p1, p2) {
2389 return Math.sqrt(sqdist(p1, p2));
2390};
2391var sqdist = function sqdist(p1, p2) {
2392 var dx = p2.x - p1.x;
2393 var dy = p2.y - p1.y;
2394 return dx * dx + dy * dy;
2395};
2396var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
2397 var length = v.length; // First, get sum of all elements
2398
2399 var total = 0;
2400
2401 for (var i = 0; i < length; i++) {
2402 total += v[i];
2403 } // Now, divide each by the sum of all elements
2404
2405
2406 for (var _i = 0; _i < length; _i++) {
2407 v[_i] = v[_i] / total;
2408 }
2409
2410 return v;
2411};
2412
2413var qbezierAt = function qbezierAt(p0, p1, p2, t) {
2414 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
2415};
2416var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
2417 return {
2418 x: qbezierAt(p0.x, p1.x, p2.x, t),
2419 y: qbezierAt(p0.y, p1.y, p2.y, t)
2420 };
2421};
2422var lineAt = function lineAt(p0, p1, t, d) {
2423 var vec = {
2424 x: p1.x - p0.x,
2425 y: p1.y - p0.y
2426 };
2427 var vecDist = dist(p0, p1);
2428 var normVec = {
2429 x: vec.x / vecDist,
2430 y: vec.y / vecDist
2431 };
2432 t = t == null ? 0 : t;
2433 d = d != null ? d : t * vecDist;
2434 return {
2435 x: p0.x + normVec.x * d,
2436 y: p0.y + normVec.y * d
2437 };
2438};
2439var bound = function bound(min, val, max) {
2440 return Math.max(min, Math.min(max, val));
2441}; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
2442
2443var makeBoundingBox = function makeBoundingBox(bb) {
2444 if (bb == null) {
2445 return {
2446 x1: Infinity,
2447 y1: Infinity,
2448 x2: -Infinity,
2449 y2: -Infinity,
2450 w: 0,
2451 h: 0
2452 };
2453 } else if (bb.x1 != null && bb.y1 != null) {
2454 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
2455 return {
2456 x1: bb.x1,
2457 y1: bb.y1,
2458 x2: bb.x2,
2459 y2: bb.y2,
2460 w: bb.x2 - bb.x1,
2461 h: bb.y2 - bb.y1
2462 };
2463 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
2464 return {
2465 x1: bb.x1,
2466 y1: bb.y1,
2467 x2: bb.x1 + bb.w,
2468 y2: bb.y1 + bb.h,
2469 w: bb.w,
2470 h: bb.h
2471 };
2472 }
2473 }
2474};
2475var copyBoundingBox = function copyBoundingBox(bb) {
2476 return {
2477 x1: bb.x1,
2478 x2: bb.x2,
2479 w: bb.w,
2480 y1: bb.y1,
2481 y2: bb.y2,
2482 h: bb.h
2483 };
2484};
2485var clearBoundingBox = function clearBoundingBox(bb) {
2486 bb.x1 = Infinity;
2487 bb.y1 = Infinity;
2488 bb.x2 = -Infinity;
2489 bb.y2 = -Infinity;
2490 bb.w = 0;
2491 bb.h = 0;
2492};
2493var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
2494 // update bb1 with bb2 bounds
2495 bb1.x1 = Math.min(bb1.x1, bb2.x1);
2496 bb1.x2 = Math.max(bb1.x2, bb2.x2);
2497 bb1.w = bb1.x2 - bb1.x1;
2498 bb1.y1 = Math.min(bb1.y1, bb2.y1);
2499 bb1.y2 = Math.max(bb1.y2, bb2.y2);
2500 bb1.h = bb1.y2 - bb1.y1;
2501};
2502var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
2503 bb.x1 = Math.min(bb.x1, x);
2504 bb.x2 = Math.max(bb.x2, x);
2505 bb.w = bb.x2 - bb.x1;
2506 bb.y1 = Math.min(bb.y1, y);
2507 bb.y2 = Math.max(bb.y2, y);
2508 bb.h = bb.y2 - bb.y1;
2509};
2510var expandBoundingBox = function expandBoundingBox(bb) {
2511 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2512 bb.x1 -= padding;
2513 bb.x2 += padding;
2514 bb.y1 -= padding;
2515 bb.y2 += padding;
2516 bb.w = bb.x2 - bb.x1;
2517 bb.h = bb.y2 - bb.y1;
2518 return bb;
2519};
2520var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
2521 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
2522 var top, right, bottom, left;
2523
2524 if (padding.length === 1) {
2525 top = right = bottom = left = padding[0];
2526 } else if (padding.length === 2) {
2527 top = bottom = padding[0];
2528 left = right = padding[1];
2529 } else if (padding.length === 4) {
2530 var _padding = _slicedToArray(padding, 4);
2531
2532 top = _padding[0];
2533 right = _padding[1];
2534 bottom = _padding[2];
2535 left = _padding[3];
2536 }
2537
2538 bb.x1 -= left;
2539 bb.x2 += right;
2540 bb.y1 -= top;
2541 bb.y2 += bottom;
2542 bb.w = bb.x2 - bb.x1;
2543 bb.h = bb.y2 - bb.y1;
2544 return bb;
2545};
2546
2547var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
2548 bb1.x1 = bb2.x1;
2549 bb1.y1 = bb2.y1;
2550 bb1.x2 = bb2.x2;
2551 bb1.y2 = bb2.y2;
2552 bb1.w = bb1.x2 - bb1.x1;
2553 bb1.h = bb1.y2 - bb1.y1;
2554};
2555var assignShiftToBoundingBox = function assignShiftToBoundingBox(bb, delta) {
2556 bb.x1 += delta.x;
2557 bb.x2 += delta.x;
2558 bb.y1 += delta.y;
2559 bb.y2 += delta.y;
2560};
2561var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
2562 // case: one bb to right of other
2563 if (bb1.x1 > bb2.x2) {
2564 return false;
2565 }
2566
2567 if (bb2.x1 > bb1.x2) {
2568 return false;
2569 } // case: one bb to left of other
2570
2571
2572 if (bb1.x2 < bb2.x1) {
2573 return false;
2574 }
2575
2576 if (bb2.x2 < bb1.x1) {
2577 return false;
2578 } // case: one bb above other
2579
2580
2581 if (bb1.y2 < bb2.y1) {
2582 return false;
2583 }
2584
2585 if (bb2.y2 < bb1.y1) {
2586 return false;
2587 } // case: one bb below other
2588
2589
2590 if (bb1.y1 > bb2.y2) {
2591 return false;
2592 }
2593
2594 if (bb2.y1 > bb1.y2) {
2595 return false;
2596 } // otherwise, must have some overlap
2597
2598
2599 return true;
2600};
2601var inBoundingBox = function inBoundingBox(bb, x, y) {
2602 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
2603};
2604var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
2605 return inBoundingBox(bb, pt.x, pt.y);
2606};
2607var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
2608 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
2609};
2610var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
2611 var cornerRadius = getRoundRectangleRadius(width, height);
2612 var halfWidth = width / 2;
2613 var halfHeight = height / 2; // Check intersections with straight line segments
2614
2615 var straightLineIntersections; // Top segment, left to right
2616
2617 {
2618 var topStartX = nodeX - halfWidth + cornerRadius - padding;
2619 var topStartY = nodeY - halfHeight - padding;
2620 var topEndX = nodeX + halfWidth - cornerRadius + padding;
2621 var topEndY = topStartY;
2622 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
2623
2624 if (straightLineIntersections.length > 0) {
2625 return straightLineIntersections;
2626 }
2627 } // Right segment, top to bottom
2628
2629 {
2630 var rightStartX = nodeX + halfWidth + padding;
2631 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
2632 var rightEndX = rightStartX;
2633 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
2634 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
2635
2636 if (straightLineIntersections.length > 0) {
2637 return straightLineIntersections;
2638 }
2639 } // Bottom segment, left to right
2640
2641 {
2642 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
2643 var bottomStartY = nodeY + halfHeight + padding;
2644 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
2645 var bottomEndY = bottomStartY;
2646 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
2647
2648 if (straightLineIntersections.length > 0) {
2649 return straightLineIntersections;
2650 }
2651 } // Left segment, top to bottom
2652
2653 {
2654 var leftStartX = nodeX - halfWidth - padding;
2655 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
2656 var leftEndX = leftStartX;
2657 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
2658 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
2659
2660 if (straightLineIntersections.length > 0) {
2661 return straightLineIntersections;
2662 }
2663 } // Check intersections with arc segments
2664
2665 var arcIntersections; // Top Left
2666
2667 {
2668 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
2669 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
2670 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2671
2672 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
2673 return [arcIntersections[0], arcIntersections[1]];
2674 }
2675 } // Top Right
2676
2677 {
2678 var topRightCenterX = nodeX + halfWidth - cornerRadius;
2679 var topRightCenterY = nodeY - halfHeight + cornerRadius;
2680 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2681
2682 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
2683 return [arcIntersections[0], arcIntersections[1]];
2684 }
2685 } // Bottom Right
2686
2687 {
2688 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
2689 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
2690 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2691
2692 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
2693 return [arcIntersections[0], arcIntersections[1]];
2694 }
2695 } // Bottom Left
2696
2697 {
2698 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
2699 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
2700 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2701
2702 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
2703 return [arcIntersections[0], arcIntersections[1]];
2704 }
2705 }
2706 return []; // if nothing
2707};
2708var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
2709 var t = tolerance;
2710 var x1 = Math.min(lx1, lx2);
2711 var x2 = Math.max(lx1, lx2);
2712 var y1 = Math.min(ly1, ly2);
2713 var y2 = Math.max(ly1, ly2);
2714 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
2715};
2716var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
2717 var bb = {
2718 x1: Math.min(x1, x3, x2) - tolerance,
2719 x2: Math.max(x1, x3, x2) + tolerance,
2720 y1: Math.min(y1, y3, y2) - tolerance,
2721 y2: Math.max(y1, y3, y2) + tolerance
2722 }; // if outside the rough bounding box for the bezier, then it can't be a hit
2723
2724 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
2725 // console.log('bezier out of rough bb')
2726 return false;
2727 } else {
2728 // console.log('do more expensive check');
2729 return true;
2730 }
2731};
2732var solveQuadratic = function solveQuadratic(a, b, c, val) {
2733 c -= val;
2734 var r = b * b - 4 * a * c;
2735
2736 if (r < 0) {
2737 return [];
2738 }
2739
2740 var sqrtR = Math.sqrt(r);
2741 var denom = 2 * a;
2742 var root1 = (-b + sqrtR) / denom;
2743 var root2 = (-b - sqrtR) / denom;
2744 return [root1, root2];
2745};
2746var solveCubic = function solveCubic(a, b, c, d, result) {
2747 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
2748 // r is the real component, i is the imaginary component
2749 // An implementation of the Cardano method from the year 1545
2750 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
2751 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
2752
2753 if (a === 0) {
2754 a = epsilon;
2755 }
2756
2757 b /= a;
2758 c /= a;
2759 d /= a;
2760 var discriminant, q, r, dum1, s, t, term1, r13;
2761 q = (3.0 * c - b * b) / 9.0;
2762 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
2763 r /= 54.0;
2764 discriminant = q * q * q + r * r;
2765 result[1] = 0;
2766 term1 = b / 3.0;
2767
2768 if (discriminant > 0) {
2769 s = r + Math.sqrt(discriminant);
2770 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
2771 t = r - Math.sqrt(discriminant);
2772 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
2773 result[0] = -term1 + s + t;
2774 term1 += (s + t) / 2.0;
2775 result[4] = result[2] = -term1;
2776 term1 = Math.sqrt(3.0) * (-t + s) / 2;
2777 result[3] = term1;
2778 result[5] = -term1;
2779 return;
2780 }
2781
2782 result[5] = result[3] = 0;
2783
2784 if (discriminant === 0) {
2785 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
2786 result[0] = -term1 + 2.0 * r13;
2787 result[4] = result[2] = -(r13 + term1);
2788 return;
2789 }
2790
2791 q = -q;
2792 dum1 = q * q * q;
2793 dum1 = Math.acos(r / Math.sqrt(dum1));
2794 r13 = 2.0 * Math.sqrt(q);
2795 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
2796 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
2797 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
2798 return;
2799};
2800var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
2801 // Find minimum distance by using the minimum of the distance
2802 // function between the given point and the curve
2803 // This gives the coefficients of the resulting cubic equation
2804 // whose roots tell us where a possible minimum is
2805 // (Coefficients are divided by 4)
2806 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;
2807 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;
2808 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;
2809 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);
2810
2811 var roots = []; // Use the cubic solving algorithm
2812
2813 solveCubic(a, b, c, d, roots);
2814 var zeroThreshold = 0.0000001;
2815 var params = [];
2816
2817 for (var index = 0; index < 6; index += 2) {
2818 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
2819 params.push(roots[index]);
2820 }
2821 }
2822
2823 params.push(1.0);
2824 params.push(0.0);
2825 var minDistanceSquared = -1;
2826 var curX, curY, distSquared;
2827
2828 for (var i = 0; i < params.length; i++) {
2829 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
2830 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
2831 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
2832
2833 if (minDistanceSquared >= 0) {
2834 if (distSquared < minDistanceSquared) {
2835 minDistanceSquared = distSquared;
2836 }
2837 } else {
2838 minDistanceSquared = distSquared;
2839 }
2840 }
2841
2842 return minDistanceSquared;
2843};
2844var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
2845 var offset = [x - x1, y - y1];
2846 var line = [x2 - x1, y2 - y1];
2847 var lineSq = line[0] * line[0] + line[1] * line[1];
2848 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
2849 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
2850 var adjSq = dotProduct * dotProduct / lineSq;
2851
2852 if (dotProduct < 0) {
2853 return hypSq;
2854 }
2855
2856 if (adjSq > lineSq) {
2857 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
2858 }
2859
2860 return hypSq - adjSq;
2861};
2862var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
2863 var x1, y1, x2, y2;
2864 var y3; // Intersect with vertical line through (x, y)
2865
2866 var up = 0; // let down = 0;
2867
2868 for (var i = 0; i < points.length / 2; i++) {
2869 x1 = points[i * 2];
2870 y1 = points[i * 2 + 1];
2871
2872 if (i + 1 < points.length / 2) {
2873 x2 = points[(i + 1) * 2];
2874 y2 = points[(i + 1) * 2 + 1];
2875 } else {
2876 x2 = points[(i + 1 - points.length / 2) * 2];
2877 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
2878 }
2879
2880 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
2881 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
2882
2883 if (y3 > y) {
2884 up++;
2885 } // if( y3 < y ){
2886 // down++;
2887 // }
2888
2889 } else {
2890 continue;
2891 }
2892 }
2893
2894 if (up % 2 === 0) {
2895 return false;
2896 } else {
2897 return true;
2898 }
2899};
2900var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
2901 var transformedPoints = new Array(basePoints.length); // Gives negative angle
2902
2903 var angle;
2904
2905 if (direction[0] != null) {
2906 angle = Math.atan(direction[1] / direction[0]);
2907
2908 if (direction[0] < 0) {
2909 angle = angle + Math.PI / 2;
2910 } else {
2911 angle = -angle - Math.PI / 2;
2912 }
2913 } else {
2914 angle = direction;
2915 }
2916
2917 var cos = Math.cos(-angle);
2918 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
2919
2920 for (var i = 0; i < transformedPoints.length / 2; i++) {
2921 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
2922 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
2923 transformedPoints[i * 2] += centerX;
2924 transformedPoints[i * 2 + 1] += centerY;
2925 }
2926
2927 var points;
2928
2929 if (padding > 0) {
2930 var expandedLineSet = expandPolygon(transformedPoints, -padding);
2931 points = joinLines(expandedLineSet);
2932 } else {
2933 points = transformedPoints;
2934 }
2935
2936 return pointInsidePolygonPoints(x, y, points);
2937};
2938var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
2939 var cutPolygonPoints = new Array(basePoints.length);
2940 var halfW = width / 2;
2941 var halfH = height / 2;
2942 var cornerRadius = getRoundPolygonRadius(width, height);
2943 var squaredCornerRadius = cornerRadius * cornerRadius;
2944
2945 for (var i = 0; i < basePoints.length / 4; i++) {
2946 var sourceUv = void 0,
2947 destUv = void 0;
2948
2949 if (i === 0) {
2950 sourceUv = basePoints.length - 2;
2951 } else {
2952 sourceUv = i * 4 - 2;
2953 }
2954
2955 destUv = i * 4 + 2;
2956 var px = centerX + halfW * basePoints[i * 4];
2957 var py = centerY + halfH * basePoints[i * 4 + 1];
2958 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
2959 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
2960 var cp0x = px - offset * basePoints[sourceUv];
2961 var cp0y = py - offset * basePoints[sourceUv + 1];
2962 var cp1x = px + offset * basePoints[destUv];
2963 var cp1y = py + offset * basePoints[destUv + 1];
2964 cutPolygonPoints[i * 4] = cp0x;
2965 cutPolygonPoints[i * 4 + 1] = cp0y;
2966 cutPolygonPoints[i * 4 + 2] = cp1x;
2967 cutPolygonPoints[i * 4 + 3] = cp1y;
2968 var orthx = basePoints[sourceUv + 1];
2969 var orthy = -basePoints[sourceUv];
2970 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
2971
2972 if (cosAlpha < 0) {
2973 orthx *= -1;
2974 orthy *= -1;
2975 }
2976
2977 var cx = cp0x + orthx * cornerRadius;
2978 var cy = cp0y + orthy * cornerRadius;
2979 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
2980
2981 if (squaredDistance <= squaredCornerRadius) {
2982 return true;
2983 }
2984 }
2985
2986 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
2987};
2988var joinLines = function joinLines(lineSet) {
2989 var vertices = new Array(lineSet.length / 2);
2990 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
2991 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
2992
2993 for (var i = 0; i < lineSet.length / 4; i++) {
2994 currentLineStartX = lineSet[i * 4];
2995 currentLineStartY = lineSet[i * 4 + 1];
2996 currentLineEndX = lineSet[i * 4 + 2];
2997 currentLineEndY = lineSet[i * 4 + 3];
2998
2999 if (i < lineSet.length / 4 - 1) {
3000 nextLineStartX = lineSet[(i + 1) * 4];
3001 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3002 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3003 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3004 } else {
3005 nextLineStartX = lineSet[0];
3006 nextLineStartY = lineSet[1];
3007 nextLineEndX = lineSet[2];
3008 nextLineEndY = lineSet[3];
3009 }
3010
3011 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3012 vertices[i * 2] = intersection[0];
3013 vertices[i * 2 + 1] = intersection[1];
3014 }
3015
3016 return vertices;
3017};
3018var expandPolygon = function expandPolygon(points, pad) {
3019 var expandedLineSet = new Array(points.length * 2);
3020 var currentPointX, currentPointY, nextPointX, nextPointY;
3021
3022 for (var i = 0; i < points.length / 2; i++) {
3023 currentPointX = points[i * 2];
3024 currentPointY = points[i * 2 + 1];
3025
3026 if (i < points.length / 2 - 1) {
3027 nextPointX = points[(i + 1) * 2];
3028 nextPointY = points[(i + 1) * 2 + 1];
3029 } else {
3030 nextPointX = points[0];
3031 nextPointY = points[1];
3032 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3033 // Assume CCW polygon winding
3034
3035
3036 var offsetX = nextPointY - currentPointY;
3037 var offsetY = -(nextPointX - currentPointX); // Normalize
3038
3039 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3040 var normalizedOffsetX = offsetX / offsetLength;
3041 var normalizedOffsetY = offsetY / offsetLength;
3042 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3043 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3044 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3045 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3046 }
3047
3048 return expandedLineSet;
3049};
3050var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3051 var dispX = centerX - x;
3052 var dispY = centerY - y;
3053 dispX /= ellipseWradius;
3054 dispY /= ellipseHradius;
3055 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3056 var newLength = len - 1;
3057
3058 if (newLength < 0) {
3059 return [];
3060 }
3061
3062 var lenProportion = newLength / len;
3063 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3064};
3065var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3066 x -= centerX;
3067 y -= centerY;
3068 x /= width / 2 + padding;
3069 y /= height / 2 + padding;
3070 return x * x + y * y <= 1;
3071}; // Returns intersections of increasing distance from line's start point
3072
3073var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3074 // Calculate d, direction vector of line
3075 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3076
3077 var f = [x1 - centerX, y1 - centerY];
3078 var a = d[0] * d[0] + d[1] * d[1];
3079 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3080 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3081 var discriminant = b * b - 4 * a * c;
3082
3083 if (discriminant < 0) {
3084 return [];
3085 }
3086
3087 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3088 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3089 var tMin = Math.min(t1, t2);
3090 var tMax = Math.max(t1, t2);
3091 var inRangeParams = [];
3092
3093 if (tMin >= 0 && tMin <= 1) {
3094 inRangeParams.push(tMin);
3095 }
3096
3097 if (tMax >= 0 && tMax <= 1) {
3098 inRangeParams.push(tMax);
3099 }
3100
3101 if (inRangeParams.length === 0) {
3102 return [];
3103 }
3104
3105 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3106 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3107
3108 if (inRangeParams.length > 1) {
3109 if (inRangeParams[0] == inRangeParams[1]) {
3110 return [nearIntersectionX, nearIntersectionY];
3111 } else {
3112 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3113 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3114 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3115 }
3116 } else {
3117 return [nearIntersectionX, nearIntersectionY];
3118 }
3119};
3120var midOfThree = function midOfThree(a, b, c) {
3121 if (b <= a && a <= c || c <= a && a <= b) {
3122 return a;
3123 } else if (a <= b && b <= c || c <= b && b <= a) {
3124 return b;
3125 } else {
3126 return c;
3127 }
3128}; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3129
3130var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3131 var dx13 = x1 - x3;
3132 var dx21 = x2 - x1;
3133 var dx43 = x4 - x3;
3134 var dy13 = y1 - y3;
3135 var dy21 = y2 - y1;
3136 var dy43 = y4 - y3;
3137 var ua_t = dx43 * dy13 - dy43 * dx13;
3138 var ub_t = dx21 * dy13 - dy21 * dx13;
3139 var u_b = dy43 * dx21 - dx43 * dy21;
3140
3141 if (u_b !== 0) {
3142 var ua = ua_t / u_b;
3143 var ub = ub_t / u_b;
3144 var flptThreshold = 0.001;
3145
3146 var _min = 0 - flptThreshold;
3147
3148 var _max = 1 + flptThreshold;
3149
3150 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3151 return [x1 + ua * dx21, y1 + ua * dy21];
3152 } else {
3153 if (!infiniteLines) {
3154 return [];
3155 } else {
3156 return [x1 + ua * dx21, y1 + ua * dy21];
3157 }
3158 }
3159 } else {
3160 if (ua_t === 0 || ub_t === 0) {
3161 // Parallel, coincident lines. Check if overlap
3162 // Check endpoint of second line
3163 if (midOfThree(x1, x2, x4) === x4) {
3164 return [x4, y4];
3165 } // Check start point of second line
3166
3167
3168 if (midOfThree(x1, x2, x3) === x3) {
3169 return [x3, y3];
3170 } // Endpoint of first line
3171
3172
3173 if (midOfThree(x3, x4, x2) === x2) {
3174 return [x2, y2];
3175 }
3176
3177 return [];
3178 } else {
3179 // Parallel, non-coincident
3180 return [];
3181 }
3182 }
3183}; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3184// intersect a node polygon (pts transformed)
3185//
3186// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3187// intersect the points (no transform)
3188
3189var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3190 var intersections = [];
3191 var intersection;
3192 var transformedPoints = new Array(basePoints.length);
3193 var doTransform = true;
3194
3195 if (width == null) {
3196 doTransform = false;
3197 }
3198
3199 var points;
3200
3201 if (doTransform) {
3202 for (var i = 0; i < transformedPoints.length / 2; i++) {
3203 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3204 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3205 }
3206
3207 if (padding > 0) {
3208 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3209 points = joinLines(expandedLineSet);
3210 } else {
3211 points = transformedPoints;
3212 }
3213 } else {
3214 points = basePoints;
3215 }
3216
3217 var currentX, currentY, nextX, nextY;
3218
3219 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3220 currentX = points[_i2 * 2];
3221 currentY = points[_i2 * 2 + 1];
3222
3223 if (_i2 < points.length / 2 - 1) {
3224 nextX = points[(_i2 + 1) * 2];
3225 nextY = points[(_i2 + 1) * 2 + 1];
3226 } else {
3227 nextX = points[0];
3228 nextY = points[1];
3229 }
3230
3231 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3232
3233 if (intersection.length !== 0) {
3234 intersections.push(intersection[0], intersection[1]);
3235 }
3236 }
3237
3238 return intersections;
3239};
3240var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3241 var intersections = [];
3242 var intersection;
3243 var lines = new Array(basePoints.length);
3244 var halfW = width / 2;
3245 var halfH = height / 2;
3246 var cornerRadius = getRoundPolygonRadius(width, height);
3247
3248 for (var i = 0; i < basePoints.length / 4; i++) {
3249 var sourceUv = void 0,
3250 destUv = void 0;
3251
3252 if (i === 0) {
3253 sourceUv = basePoints.length - 2;
3254 } else {
3255 sourceUv = i * 4 - 2;
3256 }
3257
3258 destUv = i * 4 + 2;
3259 var px = centerX + halfW * basePoints[i * 4];
3260 var py = centerY + halfH * basePoints[i * 4 + 1];
3261 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3262 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3263 var cp0x = px - offset * basePoints[sourceUv];
3264 var cp0y = py - offset * basePoints[sourceUv + 1];
3265 var cp1x = px + offset * basePoints[destUv];
3266 var cp1y = py + offset * basePoints[destUv + 1];
3267
3268 if (i === 0) {
3269 lines[basePoints.length - 2] = cp0x;
3270 lines[basePoints.length - 1] = cp0y;
3271 } else {
3272 lines[i * 4 - 2] = cp0x;
3273 lines[i * 4 - 1] = cp0y;
3274 }
3275
3276 lines[i * 4] = cp1x;
3277 lines[i * 4 + 1] = cp1y;
3278 var orthx = basePoints[sourceUv + 1];
3279 var orthy = -basePoints[sourceUv];
3280 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3281
3282 if (cosAlpha < 0) {
3283 orthx *= -1;
3284 orthy *= -1;
3285 }
3286
3287 var cx = cp0x + orthx * cornerRadius;
3288 var cy = cp0y + orthy * cornerRadius;
3289 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
3290
3291 if (intersection.length !== 0) {
3292 intersections.push(intersection[0], intersection[1]);
3293 }
3294 }
3295
3296 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
3297 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
3298
3299 if (intersection.length !== 0) {
3300 intersections.push(intersection[0], intersection[1]);
3301 }
3302 }
3303
3304 if (intersections.length > 2) {
3305 var lowestIntersection = [intersections[0], intersections[1]];
3306 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3307
3308 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
3309 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
3310
3311 if (squaredDistance <= lowestSquaredDistance) {
3312 lowestIntersection[0] = intersections[_i4 * 2];
3313 lowestIntersection[1] = intersections[_i4 * 2 + 1];
3314 lowestSquaredDistance = squaredDistance;
3315 }
3316 }
3317
3318 return lowestIntersection;
3319 }
3320
3321 return intersections;
3322};
3323var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3324 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3325 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3326 var lenRatio = (length - amount) / length;
3327
3328 if (lenRatio < 0) {
3329 lenRatio = 0.00001;
3330 }
3331
3332 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3333};
3334var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3335 var points = generateUnitNgonPoints(sides, rotationRadians);
3336 points = fitPolygonToSquare(points);
3337 return points;
3338};
3339var fitPolygonToSquare = function fitPolygonToSquare(points) {
3340 var x, y;
3341 var sides = points.length / 2;
3342 var minX = Infinity,
3343 minY = Infinity,
3344 maxX = -Infinity,
3345 maxY = -Infinity;
3346
3347 for (var i = 0; i < sides; i++) {
3348 x = points[2 * i];
3349 y = points[2 * i + 1];
3350 minX = Math.min(minX, x);
3351 maxX = Math.max(maxX, x);
3352 minY = Math.min(minY, y);
3353 maxY = Math.max(maxY, y);
3354 } // stretch factors
3355
3356
3357 var sx = 2 / (maxX - minX);
3358 var sy = 2 / (maxY - minY);
3359
3360 for (var _i5 = 0; _i5 < sides; _i5++) {
3361 x = points[2 * _i5] = points[2 * _i5] * sx;
3362 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
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 }
3368
3369 if (minY < -1) {
3370 for (var _i6 = 0; _i6 < sides; _i6++) {
3371 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
3372 }
3373 }
3374
3375 return points;
3376};
3377var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
3378 var increment = 1.0 / sides * 2 * Math.PI;
3379 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
3380 startAngle += rotationRadians;
3381 var points = new Array(sides * 2);
3382 var currentAngle;
3383
3384 for (var i = 0; i < sides; i++) {
3385 currentAngle = i * increment + startAngle;
3386 points[2 * i] = Math.cos(currentAngle); // x
3387
3388 points[2 * i + 1] = Math.sin(-currentAngle); // y
3389 }
3390
3391 return points;
3392}; // Set the default radius, unless half of width or height is smaller than default
3393
3394var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
3395 return Math.min(width / 4, height / 4, 8);
3396}; // Set the default radius
3397
3398var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
3399 return Math.min(width / 10, height / 10, 8);
3400};
3401var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
3402 return 8;
3403};
3404var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
3405 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
3406}; // get curve width, height, and control point position offsets as a percentage of node height / width
3407
3408var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
3409 return {
3410 heightOffset: Math.min(15, 0.05 * height),
3411 widthOffset: Math.min(100, 0.25 * width),
3412 ctrlPtOffsetPct: 0.05
3413 };
3414};
3415
3416var pageRankDefaults = defaults({
3417 dampingFactor: 0.8,
3418 precision: 0.000001,
3419 iterations: 200,
3420 weight: function weight(edge) {
3421 return 1;
3422 }
3423});
3424var elesfn$7 = {
3425 pageRank: function pageRank(options) {
3426 var _pageRankDefaults = pageRankDefaults(options),
3427 dampingFactor = _pageRankDefaults.dampingFactor,
3428 precision = _pageRankDefaults.precision,
3429 iterations = _pageRankDefaults.iterations,
3430 weight = _pageRankDefaults.weight;
3431
3432 var cy = this._private.cy;
3433
3434 var _this$byGroup = this.byGroup(),
3435 nodes = _this$byGroup.nodes,
3436 edges = _this$byGroup.edges;
3437
3438 var numNodes = nodes.length;
3439 var numNodesSqd = numNodes * numNodes;
3440 var numEdges = edges.length; // Construct transposed adjacency matrix
3441 // First lets have a zeroed matrix of the right size
3442 // We'll also keep track of the sum of each column
3443
3444 var matrix = new Array(numNodesSqd);
3445 var columnSum = new Array(numNodes);
3446 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
3447
3448 for (var i = 0; i < numNodes; i++) {
3449 for (var j = 0; j < numNodes; j++) {
3450 var n = i * numNodes + j;
3451 matrix[n] = 0;
3452 }
3453
3454 columnSum[i] = 0;
3455 } // Now, process edges
3456
3457
3458 for (var _i = 0; _i < numEdges; _i++) {
3459 var edge = edges[_i];
3460 var srcId = edge.data('source');
3461 var tgtId = edge.data('target'); // Don't include loops in the matrix
3462
3463 if (srcId === tgtId) {
3464 continue;
3465 }
3466
3467 var s = nodes.indexOfId(srcId);
3468 var t = nodes.indexOfId(tgtId);
3469 var w = weight(edge);
3470
3471 var _n = t * numNodes + s; // Update matrix
3472
3473
3474 matrix[_n] += w; // Update column sum
3475
3476 columnSum[s] += w;
3477 } // Add additional probability based on damping factor
3478 // Also, take into account columns that have sum = 0
3479
3480
3481 var p = 1.0 / numNodes + additionalProb; // Shorthand
3482 // Traverse matrix, column by column
3483
3484 for (var _j = 0; _j < numNodes; _j++) {
3485 if (columnSum[_j] === 0) {
3486 // No 'links' out from node jth, assume equal probability for each possible node
3487 for (var _i2 = 0; _i2 < numNodes; _i2++) {
3488 var _n2 = _i2 * numNodes + _j;
3489
3490 matrix[_n2] = p;
3491 }
3492 } else {
3493 // Node jth has outgoing link, compute normalized probabilities
3494 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3495 var _n3 = _i3 * numNodes + _j;
3496
3497 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
3498 }
3499 }
3500 } // Compute dominant eigenvector using power method
3501
3502
3503 var eigenvector = new Array(numNodes);
3504 var temp = new Array(numNodes);
3505 var previous; // Start with a vector of all 1's
3506 // Also, initialize a null vector which will be used as shorthand
3507
3508 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3509 eigenvector[_i4] = 1;
3510 }
3511
3512 for (var iter = 0; iter < iterations; iter++) {
3513 // Temp array with all 0's
3514 for (var _i5 = 0; _i5 < numNodes; _i5++) {
3515 temp[_i5] = 0;
3516 } // Multiply matrix with previous result
3517
3518
3519 for (var _i6 = 0; _i6 < numNodes; _i6++) {
3520 for (var _j2 = 0; _j2 < numNodes; _j2++) {
3521 var _n4 = _i6 * numNodes + _j2;
3522
3523 temp[_i6] += matrix[_n4] * eigenvector[_j2];
3524 }
3525 }
3526
3527 inPlaceSumNormalize(temp);
3528 previous = eigenvector;
3529 eigenvector = temp;
3530 temp = previous;
3531 var diff = 0; // Compute difference (squared module) of both vectors
3532
3533 for (var _i7 = 0; _i7 < numNodes; _i7++) {
3534 var delta = previous[_i7] - eigenvector[_i7];
3535 diff += delta * delta;
3536 } // If difference is less than the desired threshold, stop iterating
3537
3538
3539 if (diff < precision) {
3540 break;
3541 }
3542 } // Construct result
3543
3544
3545 var res = {
3546 rank: function rank(node) {
3547 node = cy.collection(node)[0];
3548 return eigenvector[nodes.indexOf(node)];
3549 }
3550 };
3551 return res;
3552 } // pageRank
3553
3554}; // elesfn
3555
3556var defaults$1 = defaults({
3557 root: null,
3558 weight: function weight(edge) {
3559 return 1;
3560 },
3561 directed: false,
3562 alpha: 0
3563});
3564var elesfn$8 = {
3565 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
3566 options = defaults$1(options);
3567 var cy = this.cy();
3568 var nodes = this.nodes();
3569 var numNodes = nodes.length;
3570
3571 if (!options.directed) {
3572 var degrees = {};
3573 var maxDegree = 0;
3574
3575 for (var i = 0; i < numNodes; i++) {
3576 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
3577
3578 options.root = node;
3579 var currDegree = this.degreeCentrality(options);
3580
3581 if (maxDegree < currDegree.degree) {
3582 maxDegree = currDegree.degree;
3583 }
3584
3585 degrees[node.id()] = currDegree.degree;
3586 }
3587
3588 return {
3589 degree: function degree(node) {
3590 if (maxDegree === 0) {
3591 return 0;
3592 }
3593
3594 if (string(node)) {
3595 // from is a selector string
3596 node = cy.filter(node);
3597 }
3598
3599 return degrees[node.id()] / maxDegree;
3600 }
3601 };
3602 } else {
3603 var indegrees = {};
3604 var outdegrees = {};
3605 var maxIndegree = 0;
3606 var maxOutdegree = 0;
3607
3608 for (var _i = 0; _i < numNodes; _i++) {
3609 var _node = nodes[_i];
3610
3611 var id = _node.id(); // add current node to the current options object and call degreeCentrality
3612
3613
3614 options.root = _node;
3615
3616 var _currDegree = this.degreeCentrality(options);
3617
3618 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
3619 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
3620 indegrees[id] = _currDegree.indegree;
3621 outdegrees[id] = _currDegree.outdegree;
3622 }
3623
3624 return {
3625 indegree: function indegree(node) {
3626 if (maxIndegree == 0) {
3627 return 0;
3628 }
3629
3630 if (string(node)) {
3631 // from is a selector string
3632 node = cy.filter(node);
3633 }
3634
3635 return indegrees[node.id()] / maxIndegree;
3636 },
3637 outdegree: function outdegree(node) {
3638 if (maxOutdegree === 0) {
3639 return 0;
3640 }
3641
3642 if (string(node)) {
3643 // from is a selector string
3644 node = cy.filter(node);
3645 }
3646
3647 return outdegrees[node.id()] / maxOutdegree;
3648 }
3649 };
3650 }
3651 },
3652 // degreeCentralityNormalized
3653 // Implemented from the algorithm in Opsahl's paper
3654 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
3655 // check the heading 2 "Degree"
3656 degreeCentrality: function degreeCentrality(options) {
3657 options = defaults$1(options);
3658 var cy = this.cy();
3659 var callingEles = this;
3660 var _options = options,
3661 root = _options.root,
3662 weight = _options.weight,
3663 directed = _options.directed,
3664 alpha = _options.alpha;
3665 root = cy.collection(root)[0];
3666
3667 if (!directed) {
3668 var connEdges = root.connectedEdges().intersection(callingEles);
3669 var k = connEdges.length;
3670 var s = 0; // Now, sum edge weights
3671
3672 for (var i = 0; i < connEdges.length; i++) {
3673 s += weight(connEdges[i]);
3674 }
3675
3676 return {
3677 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
3678 };
3679 } else {
3680 var edges = root.connectedEdges();
3681 var incoming = edges.filter(function (edge) {
3682 return edge.target().same(root) && callingEles.has(edge);
3683 });
3684 var outgoing = edges.filter(function (edge) {
3685 return edge.source().same(root) && callingEles.has(edge);
3686 });
3687 var k_in = incoming.length;
3688 var k_out = outgoing.length;
3689 var s_in = 0;
3690 var s_out = 0; // Now, sum incoming edge weights
3691
3692 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
3693 s_in += weight(incoming[_i2]);
3694 } // Now, sum outgoing edge weights
3695
3696
3697 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
3698 s_out += weight(outgoing[_i3]);
3699 }
3700
3701 return {
3702 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
3703 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
3704 };
3705 }
3706 } // degreeCentrality
3707
3708}; // elesfn
3709// nice, short mathemathical alias
3710
3711elesfn$8.dc = elesfn$8.degreeCentrality;
3712elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
3713
3714var defaults$2 = defaults({
3715 harmonic: true,
3716 weight: function weight() {
3717 return 1;
3718 },
3719 directed: false,
3720 root: null
3721});
3722var elesfn$9 = {
3723 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
3724 var _defaults = defaults$2(options),
3725 harmonic = _defaults.harmonic,
3726 weight = _defaults.weight,
3727 directed = _defaults.directed;
3728
3729 var cy = this.cy();
3730 var closenesses = {};
3731 var maxCloseness = 0;
3732 var nodes = this.nodes();
3733 var fw = this.floydWarshall({
3734 weight: weight,
3735 directed: directed
3736 }); // Compute closeness for every node and find the maximum closeness
3737
3738 for (var i = 0; i < nodes.length; i++) {
3739 var currCloseness = 0;
3740 var node_i = nodes[i];
3741
3742 for (var j = 0; j < nodes.length; j++) {
3743 if (i !== j) {
3744 var d = fw.distance(node_i, nodes[j]);
3745
3746 if (harmonic) {
3747 currCloseness += 1 / d;
3748 } else {
3749 currCloseness += d;
3750 }
3751 }
3752 }
3753
3754 if (!harmonic) {
3755 currCloseness = 1 / currCloseness;
3756 }
3757
3758 if (maxCloseness < currCloseness) {
3759 maxCloseness = currCloseness;
3760 }
3761
3762 closenesses[node_i.id()] = currCloseness;
3763 }
3764
3765 return {
3766 closeness: function closeness(node) {
3767 if (maxCloseness == 0) {
3768 return 0;
3769 }
3770
3771 if (string(node)) {
3772 // from is a selector string
3773 node = cy.filter(node)[0].id();
3774 } else {
3775 // from is a node
3776 node = node.id();
3777 }
3778
3779 return closenesses[node] / maxCloseness;
3780 }
3781 };
3782 },
3783 // Implemented from pseudocode from wikipedia
3784 closenessCentrality: function closenessCentrality(options) {
3785 var _defaults2 = defaults$2(options),
3786 root = _defaults2.root,
3787 weight = _defaults2.weight,
3788 directed = _defaults2.directed,
3789 harmonic = _defaults2.harmonic;
3790
3791 root = this.filter(root)[0]; // we need distance from this node to every other node
3792
3793 var dijkstra = this.dijkstra({
3794 root: root,
3795 weight: weight,
3796 directed: directed
3797 });
3798 var totalDistance = 0;
3799 var nodes = this.nodes();
3800
3801 for (var i = 0; i < nodes.length; i++) {
3802 var n = nodes[i];
3803
3804 if (!n.same(root)) {
3805 var d = dijkstra.distanceTo(n);
3806
3807 if (harmonic) {
3808 totalDistance += 1 / d;
3809 } else {
3810 totalDistance += d;
3811 }
3812 }
3813 }
3814
3815 return harmonic ? totalDistance : 1 / totalDistance;
3816 } // closenessCentrality
3817
3818}; // elesfn
3819// nice, short mathemathical alias
3820
3821elesfn$9.cc = elesfn$9.closenessCentrality;
3822elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
3823
3824var defaults$3 = defaults({
3825 weight: null,
3826 directed: false
3827});
3828var elesfn$a = {
3829 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
3830 betweennessCentrality: function betweennessCentrality(options) {
3831 var _defaults = defaults$3(options),
3832 directed = _defaults.directed,
3833 weight = _defaults.weight;
3834
3835 var weighted = weight != null;
3836 var cy = this.cy(); // starting
3837
3838 var V = this.nodes();
3839 var A = {};
3840 var _C = {};
3841 var max = 0;
3842 var C = {
3843 set: function set(key, val) {
3844 _C[key] = val;
3845
3846 if (val > max) {
3847 max = val;
3848 }
3849 },
3850 get: function get(key) {
3851 return _C[key];
3852 }
3853 }; // A contains the neighborhoods of every node
3854
3855 for (var i = 0; i < V.length; i++) {
3856 var v = V[i];
3857 var vid = v.id();
3858
3859 if (directed) {
3860 A[vid] = v.outgoers().nodes(); // get outgoers of every node
3861 } else {
3862 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
3863 }
3864
3865 C.set(vid, 0);
3866 }
3867
3868 var _loop = function _loop(s) {
3869 var sid = V[s].id();
3870 var S = []; // stack
3871
3872 var P = {};
3873 var g = {};
3874 var d = {};
3875 var Q = new Heap(function (a, b) {
3876 return d[a] - d[b];
3877 }); // queue
3878 // init dictionaries
3879
3880 for (var _i = 0; _i < V.length; _i++) {
3881 var _vid = V[_i].id();
3882
3883 P[_vid] = [];
3884 g[_vid] = 0;
3885 d[_vid] = Infinity;
3886 }
3887
3888 g[sid] = 1; // sigma
3889
3890 d[sid] = 0; // distance to s
3891
3892 Q.push(sid);
3893
3894 while (!Q.empty()) {
3895 var _v = Q.pop();
3896
3897 S.push(_v);
3898
3899 if (weighted) {
3900 for (var j = 0; j < A[_v].length; j++) {
3901 var w = A[_v][j];
3902 var vEle = cy.getElementById(_v);
3903 var edge = void 0;
3904
3905 if (vEle.edgesTo(w).length > 0) {
3906 edge = vEle.edgesTo(w)[0];
3907 } else {
3908 edge = w.edgesTo(vEle)[0];
3909 }
3910
3911 var edgeWeight = weight(edge);
3912 w = w.id();
3913
3914 if (d[w] > d[_v] + edgeWeight) {
3915 d[w] = d[_v] + edgeWeight;
3916
3917 if (Q.nodes.indexOf(w) < 0) {
3918 //if w is not in Q
3919 Q.push(w);
3920 } else {
3921 // update position if w is in Q
3922 Q.updateItem(w);
3923 }
3924
3925 g[w] = 0;
3926 P[w] = [];
3927 }
3928
3929 if (d[w] == d[_v] + edgeWeight) {
3930 g[w] = g[w] + g[_v];
3931 P[w].push(_v);
3932 }
3933 }
3934 } else {
3935 for (var _j = 0; _j < A[_v].length; _j++) {
3936 var _w = A[_v][_j].id();
3937
3938 if (d[_w] == Infinity) {
3939 Q.push(_w);
3940 d[_w] = d[_v] + 1;
3941 }
3942
3943 if (d[_w] == d[_v] + 1) {
3944 g[_w] = g[_w] + g[_v];
3945
3946 P[_w].push(_v);
3947 }
3948 }
3949 }
3950 }
3951
3952 var e = {};
3953
3954 for (var _i2 = 0; _i2 < V.length; _i2++) {
3955 e[V[_i2].id()] = 0;
3956 }
3957
3958 while (S.length > 0) {
3959 var _w2 = S.pop();
3960
3961 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
3962 var _v2 = P[_w2][_j2];
3963 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
3964
3965 if (_w2 != V[s].id()) {
3966 C.set(_w2, C.get(_w2) + e[_w2]);
3967 }
3968 }
3969 }
3970 };
3971
3972 for (var s = 0; s < V.length; s++) {
3973 _loop(s);
3974 }
3975
3976 var ret = {
3977 betweenness: function betweenness(node) {
3978 var id = cy.collection(node).id();
3979 return C.get(id);
3980 },
3981 betweennessNormalized: function betweennessNormalized(node) {
3982 if (max == 0) {
3983 return 0;
3984 }
3985
3986 var id = cy.collection(node).id();
3987 return C.get(id) / max;
3988 }
3989 }; // alias
3990
3991 ret.betweennessNormalised = ret.betweennessNormalized;
3992 return ret;
3993 } // betweennessCentrality
3994
3995}; // elesfn
3996// nice, short mathemathical alias
3997
3998elesfn$a.bc = elesfn$a.betweennessCentrality;
3999
4000// Implemented by Zoe Xi @zoexi for GSOC 2016
4001/* eslint-disable no-unused-vars */
4002
4003var defaults$4 = defaults({
4004 expandFactor: 2,
4005 // affects time of computation and cluster granularity to some extent: M * M
4006 inflateFactor: 2,
4007 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4008 multFactor: 1,
4009 // optional self loops for each node. Use a neutral value to improve cluster computations.
4010 maxIterations: 20,
4011 // maximum number of iterations of the MCL algorithm in a single run
4012 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4013 function (edge) {
4014 return 1;
4015 }]
4016});
4017/* eslint-enable */
4018
4019var setOptions = function setOptions(options) {
4020 return defaults$4(options);
4021};
4022/* eslint-enable */
4023
4024
4025var getSimilarity = function getSimilarity(edge, attributes) {
4026 var total = 0;
4027
4028 for (var i = 0; i < attributes.length; i++) {
4029 total += attributes[i](edge);
4030 }
4031
4032 return total;
4033};
4034
4035var addLoops = function addLoops(M, n, val) {
4036 for (var i = 0; i < n; i++) {
4037 M[i * n + i] = val;
4038 }
4039};
4040
4041var normalize = function normalize(M, n) {
4042 var sum;
4043
4044 for (var col = 0; col < n; col++) {
4045 sum = 0;
4046
4047 for (var row = 0; row < n; row++) {
4048 sum += M[row * n + col];
4049 }
4050
4051 for (var _row = 0; _row < n; _row++) {
4052 M[_row * n + col] = M[_row * n + col] / sum;
4053 }
4054 }
4055}; // TODO: blocked matrix multiplication?
4056
4057
4058var mmult = function mmult(A, B, n) {
4059 var C = new Array(n * n);
4060
4061 for (var i = 0; i < n; i++) {
4062 for (var j = 0; j < n; j++) {
4063 C[i * n + j] = 0;
4064 }
4065
4066 for (var k = 0; k < n; k++) {
4067 for (var _j = 0; _j < n; _j++) {
4068 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4069 }
4070 }
4071 }
4072
4073 return C;
4074};
4075
4076var expand = function expand(M, n, expandFactor
4077/** power **/
4078) {
4079 var _M = M.slice(0);
4080
4081 for (var p = 1; p < expandFactor; p++) {
4082 M = mmult(M, _M, n);
4083 }
4084
4085 return M;
4086};
4087
4088var inflate = function inflate(M, n, inflateFactor
4089/** r **/
4090) {
4091 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4092
4093
4094 for (var i = 0; i < n * n; i++) {
4095 _M[i] = Math.pow(M[i], inflateFactor);
4096 }
4097
4098 normalize(_M, n);
4099 return _M;
4100};
4101
4102var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4103 // Check that both matrices have the same elements (i,j)
4104 for (var i = 0; i < n2; i++) {
4105 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4106
4107 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4108
4109 if (v1 !== v2) {
4110 return false;
4111 }
4112 }
4113
4114 return true;
4115};
4116
4117var assign = function assign(M, n, nodes, cy) {
4118 var clusters = [];
4119
4120 for (var i = 0; i < n; i++) {
4121 var cluster = [];
4122
4123 for (var j = 0; j < n; j++) {
4124 // Row-wise attractors and elements that they attract belong in same cluster
4125 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4126 cluster.push(nodes[j]);
4127 }
4128 }
4129
4130 if (cluster.length !== 0) {
4131 clusters.push(cy.collection(cluster));
4132 }
4133 }
4134
4135 return clusters;
4136};
4137
4138var isDuplicate = function isDuplicate(c1, c2) {
4139 for (var i = 0; i < c1.length; i++) {
4140 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4141 return false;
4142 }
4143 }
4144
4145 return true;
4146};
4147
4148var removeDuplicates = function removeDuplicates(clusters) {
4149 for (var i = 0; i < clusters.length; i++) {
4150 for (var j = 0; j < clusters.length; j++) {
4151 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4152 clusters.splice(j, 1);
4153 }
4154 }
4155 }
4156
4157 return clusters;
4158};
4159
4160var markovClustering = function markovClustering(options) {
4161 var nodes = this.nodes();
4162 var edges = this.edges();
4163 var cy = this.cy(); // Set parameters of algorithm:
4164
4165 var opts = setOptions(options); // Map each node to its position in node array
4166
4167 var id2position = {};
4168
4169 for (var i = 0; i < nodes.length; i++) {
4170 id2position[nodes[i].id()] = i;
4171 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4172
4173
4174 var n = nodes.length,
4175 n2 = n * n;
4176
4177 var M = new Array(n2),
4178 _M;
4179
4180 for (var _i = 0; _i < n2; _i++) {
4181 M[_i] = 0;
4182 }
4183
4184 for (var e = 0; e < edges.length; e++) {
4185 var edge = edges[e];
4186 var _i2 = id2position[edge.source().id()];
4187 var j = id2position[edge.target().id()];
4188 var sim = getSimilarity(edge, opts.attributes);
4189 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4190
4191 M[j * n + _i2] += sim;
4192 } // Begin Markov cluster algorithm
4193 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4194
4195
4196 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4197
4198 normalize(M, n);
4199 var isStillMoving = true;
4200 var iterations = 0;
4201
4202 while (isStillMoving && iterations < opts.maxIterations) {
4203 isStillMoving = false; // Step 3:
4204
4205 _M = expand(M, n, opts.expandFactor); // Step 4:
4206
4207 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4208
4209 if (!hasConverged(M, _M, n2, 4)) {
4210 isStillMoving = true;
4211 }
4212
4213 iterations++;
4214 } // Build clusters from matrix
4215
4216
4217 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4218
4219 clusters = removeDuplicates(clusters);
4220 return clusters;
4221};
4222
4223var markovClustering$1 = {
4224 markovClustering: markovClustering,
4225 mcl: markovClustering
4226};
4227
4228// Common distance metrics for clustering algorithms
4229
4230var identity = function identity(x) {
4231 return x;
4232};
4233
4234var absDiff = function absDiff(p, q) {
4235 return Math.abs(q - p);
4236};
4237
4238var addAbsDiff = function addAbsDiff(total, p, q) {
4239 return total + absDiff(p, q);
4240};
4241
4242var addSquaredDiff = function addSquaredDiff(total, p, q) {
4243 return total + Math.pow(q - p, 2);
4244};
4245
4246var sqrt = function sqrt(x) {
4247 return Math.sqrt(x);
4248};
4249
4250var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4251 return Math.max(currentMax, absDiff(p, q));
4252};
4253
4254var getDistance = function getDistance(length, getP, getQ, init, visit) {
4255 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4256 var ret = init;
4257 var p, q;
4258
4259 for (var dim = 0; dim < length; dim++) {
4260 p = getP(dim);
4261 q = getQ(dim);
4262 ret = visit(ret, p, q);
4263 }
4264
4265 return post(ret);
4266};
4267
4268var distances = {
4269 euclidean: function euclidean(length, getP, getQ) {
4270 if (length >= 2) {
4271 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4272 } else {
4273 // for single attr case, more efficient to avoid sqrt
4274 return getDistance(length, getP, getQ, 0, addAbsDiff);
4275 }
4276 },
4277 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4278 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4279 },
4280 manhattan: function manhattan(length, getP, getQ) {
4281 return getDistance(length, getP, getQ, 0, addAbsDiff);
4282 },
4283 max: function max(length, getP, getQ) {
4284 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4285 }
4286}; // in case the user accidentally doesn't use camel case
4287
4288distances['squared-euclidean'] = distances['squaredEuclidean'];
4289distances['squaredeuclidean'] = distances['squaredEuclidean'];
4290function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4291 var impl;
4292
4293 if (fn(method)) {
4294 impl = method;
4295 } else {
4296 impl = distances[method] || distances.euclidean;
4297 }
4298
4299 if (length === 0 && fn(method)) {
4300 return impl(nodeP, nodeQ);
4301 } else {
4302 return impl(length, getP, getQ, nodeP, nodeQ);
4303 }
4304}
4305
4306var defaults$5 = defaults({
4307 k: 2,
4308 m: 2,
4309 sensitivityThreshold: 0.0001,
4310 distance: 'euclidean',
4311 maxIterations: 10,
4312 attributes: [],
4313 testMode: false,
4314 testCentroids: null
4315});
4316
4317var setOptions$1 = function setOptions(options) {
4318 return defaults$5(options);
4319};
4320/* eslint-enable */
4321
4322
4323var getDist = function getDist(type, node, centroid, attributes, mode) {
4324 var noNodeP = mode !== 'kMedoids';
4325 var getP = noNodeP ? function (i) {
4326 return centroid[i];
4327 } : function (i) {
4328 return attributes[i](centroid);
4329 };
4330
4331 var getQ = function getQ(i) {
4332 return attributes[i](node);
4333 };
4334
4335 var nodeP = centroid;
4336 var nodeQ = node;
4337 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4338};
4339
4340var randomCentroids = function randomCentroids(nodes, k, attributes) {
4341 var ndim = attributes.length;
4342 var min = new Array(ndim);
4343 var max = new Array(ndim);
4344 var centroids = new Array(k);
4345 var centroid = null; // Find min, max values for each attribute dimension
4346
4347 for (var i = 0; i < ndim; i++) {
4348 min[i] = nodes.min(attributes[i]).value;
4349 max[i] = nodes.max(attributes[i]).value;
4350 } // Build k centroids, each represented as an n-dim feature vector
4351
4352
4353 for (var c = 0; c < k; c++) {
4354 centroid = [];
4355
4356 for (var _i = 0; _i < ndim; _i++) {
4357 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4358 }
4359
4360 centroids[c] = centroid;
4361 }
4362
4363 return centroids;
4364};
4365
4366var classify = function classify(node, centroids, distance, attributes, type) {
4367 var min = Infinity;
4368 var index = 0;
4369
4370 for (var i = 0; i < centroids.length; i++) {
4371 var dist = getDist(distance, node, centroids[i], attributes, type);
4372
4373 if (dist < min) {
4374 min = dist;
4375 index = i;
4376 }
4377 }
4378
4379 return index;
4380};
4381
4382var buildCluster = function buildCluster(centroid, nodes, assignment) {
4383 var cluster = [];
4384 var node = null;
4385
4386 for (var n = 0; n < nodes.length; n++) {
4387 node = nodes[n];
4388
4389 if (assignment[node.id()] === centroid) {
4390 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4391 cluster.push(node);
4392 }
4393 }
4394
4395 return cluster;
4396};
4397
4398var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4399 return Math.abs(v2 - v1) <= sensitivityThreshold;
4400};
4401
4402var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4403 for (var i = 0; i < v1.length; i++) {
4404 for (var j = 0; j < v1[i].length; j++) {
4405 var diff = Math.abs(v1[i][j] - v2[i][j]);
4406
4407 if (diff > sensitivityThreshold) {
4408 return false;
4409 }
4410 }
4411 }
4412
4413 return true;
4414};
4415
4416var seenBefore = function seenBefore(node, medoids, n) {
4417 for (var i = 0; i < n; i++) {
4418 if (node === medoids[i]) return true;
4419 }
4420
4421 return false;
4422};
4423
4424var randomMedoids = function randomMedoids(nodes, k) {
4425 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
4426 // so we need to check to see if we've already seen or chose this node before.
4427
4428 if (nodes.length < 50) {
4429 // Randomly select k medoids from the n nodes
4430 for (var i = 0; i < k; i++) {
4431 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).
4432 // Instead choose a different random node.
4433
4434 while (seenBefore(node, medoids, i)) {
4435 node = nodes[Math.floor(Math.random() * nodes.length)];
4436 }
4437
4438 medoids[i] = node;
4439 }
4440 } else {
4441 // Relatively large data set, so pretty safe to not check and just select random nodes
4442 for (var _i2 = 0; _i2 < k; _i2++) {
4443 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4444 }
4445 }
4446
4447 return medoids;
4448};
4449
4450var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4451 var cost = 0;
4452
4453 for (var n = 0; n < cluster.length; n++) {
4454 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4455 }
4456
4457 return cost;
4458};
4459
4460var kMeans = function kMeans(options) {
4461 var cy = this.cy();
4462 var nodes = this.nodes();
4463 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
4464
4465 var opts = setOptions$1(options); // Begin k-means algorithm
4466
4467 var clusters = new Array(opts.k);
4468 var assignment = {};
4469 var centroids; // Step 1: Initialize centroid positions
4470
4471 if (opts.testMode) {
4472 if (typeof opts.testCentroids === 'number') {
4473 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4474 } else if (_typeof(opts.testCentroids) === 'object') {
4475 centroids = opts.testCentroids;
4476 } else {
4477 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4478 }
4479 } else {
4480 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4481 }
4482
4483 var isStillMoving = true;
4484 var iterations = 0;
4485
4486 while (isStillMoving && iterations < opts.maxIterations) {
4487 // Step 2: Assign nodes to the nearest centroid
4488 for (var n = 0; n < nodes.length; n++) {
4489 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4490
4491 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
4492 } // Step 3: For each of the k clusters, update its centroid
4493
4494
4495 isStillMoving = false;
4496
4497 for (var c = 0; c < opts.k; c++) {
4498 // Get all nodes that belong to this cluster
4499 var cluster = buildCluster(c, nodes, assignment);
4500
4501 if (cluster.length === 0) {
4502 // If cluster is empty, break out early & move to next cluster
4503 continue;
4504 } // Update centroids by calculating avg of all nodes within the cluster.
4505
4506
4507 var ndim = opts.attributes.length;
4508 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
4509
4510 var newCentroid = new Array(ndim);
4511 var sum = new Array(ndim);
4512
4513 for (var d = 0; d < ndim; d++) {
4514 sum[d] = 0.0;
4515
4516 for (var i = 0; i < cluster.length; i++) {
4517 node = cluster[i];
4518 sum[d] += opts.attributes[d](node);
4519 }
4520
4521 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
4522
4523 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
4524 isStillMoving = true;
4525 }
4526 }
4527
4528 centroids[c] = newCentroid;
4529 clusters[c] = cy.collection(cluster);
4530 }
4531
4532 iterations++;
4533 }
4534
4535 return clusters;
4536};
4537
4538var kMedoids = function kMedoids(options) {
4539 var cy = this.cy();
4540 var nodes = this.nodes();
4541 var node = null;
4542 var opts = setOptions$1(options); // Begin k-medoids algorithm
4543
4544 var clusters = new Array(opts.k);
4545 var medoids;
4546 var assignment = {};
4547 var curCost;
4548 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
4549 // Step 1: Initialize k medoids
4550
4551 if (opts.testMode) {
4552 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
4553 medoids = opts.testCentroids;
4554 } else {
4555 medoids = randomMedoids(nodes, opts.k);
4556 }
4557 } else {
4558 medoids = randomMedoids(nodes, opts.k);
4559 }
4560
4561 var isStillMoving = true;
4562 var iterations = 0;
4563
4564 while (isStillMoving && iterations < opts.maxIterations) {
4565 // Step 2: Assign nodes to the nearest medoid
4566 for (var n = 0; n < nodes.length; n++) {
4567 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4568
4569 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
4570 }
4571
4572 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
4573 // select the node with the lowest configuration cost as new medoid.
4574
4575 for (var m = 0; m < medoids.length; m++) {
4576 // Get all nodes that belong to this medoid
4577 var cluster = buildCluster(m, nodes, assignment);
4578
4579 if (cluster.length === 0) {
4580 // If cluster is empty, break out early & move to next cluster
4581 continue;
4582 }
4583
4584 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
4585 // Select different medoid if its configuration has the lowest cost
4586
4587 for (var _n = 0; _n < cluster.length; _n++) {
4588 curCost = findCost(cluster[_n], cluster, opts.attributes);
4589
4590 if (curCost < minCosts[m]) {
4591 minCosts[m] = curCost;
4592 medoids[m] = cluster[_n];
4593 isStillMoving = true;
4594 }
4595 }
4596
4597 clusters[m] = cy.collection(cluster);
4598 }
4599
4600 iterations++;
4601 }
4602
4603 return clusters;
4604};
4605
4606var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
4607 var numerator, denominator;
4608
4609 for (var n = 0; n < nodes.length; n++) {
4610 for (var c = 0; c < centroids.length; c++) {
4611 weight[n][c] = Math.pow(U[n][c], opts.m);
4612 }
4613 }
4614
4615 for (var _c = 0; _c < centroids.length; _c++) {
4616 for (var dim = 0; dim < opts.attributes.length; dim++) {
4617 numerator = 0;
4618 denominator = 0;
4619
4620 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
4621 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
4622 denominator += weight[_n2][_c];
4623 }
4624
4625 centroids[_c][dim] = numerator / denominator;
4626 }
4627 }
4628};
4629
4630var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
4631 // Save previous step
4632 for (var i = 0; i < U.length; i++) {
4633 _U[i] = U[i].slice();
4634 }
4635
4636 var sum, numerator, denominator;
4637 var pow = 2 / (opts.m - 1);
4638
4639 for (var c = 0; c < centroids.length; c++) {
4640 for (var n = 0; n < nodes.length; n++) {
4641 sum = 0;
4642
4643 for (var k = 0; k < centroids.length; k++) {
4644 // against all other centroids
4645 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
4646 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
4647 sum += Math.pow(numerator / denominator, pow);
4648 }
4649
4650 U[n][c] = 1 / sum;
4651 }
4652 }
4653};
4654
4655var assign$1 = function assign(nodes, U, opts, cy) {
4656 var clusters = new Array(opts.k);
4657
4658 for (var c = 0; c < clusters.length; c++) {
4659 clusters[c] = [];
4660 }
4661
4662 var max;
4663 var index;
4664
4665 for (var n = 0; n < U.length; n++) {
4666 // for each node (U is N x C matrix)
4667 max = -Infinity;
4668 index = -1; // Determine which cluster the node is most likely to belong in
4669
4670 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
4671 if (U[n][_c2] > max) {
4672 max = U[n][_c2];
4673 index = _c2;
4674 }
4675 }
4676
4677 clusters[index].push(nodes[n]);
4678 } // Turn every array into a collection of nodes
4679
4680
4681 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
4682 clusters[_c3] = cy.collection(clusters[_c3]);
4683 }
4684
4685 return clusters;
4686};
4687
4688var fuzzyCMeans = function fuzzyCMeans(options) {
4689 var cy = this.cy();
4690 var nodes = this.nodes();
4691 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
4692
4693 var clusters;
4694 var centroids;
4695 var U;
4696
4697 var _U;
4698
4699 var weight; // Step 1: Initialize letiables.
4700
4701 _U = new Array(nodes.length);
4702
4703 for (var i = 0; i < nodes.length; i++) {
4704 // N x C matrix
4705 _U[i] = new Array(opts.k);
4706 }
4707
4708 U = new Array(nodes.length);
4709
4710 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
4711 // N x C matrix
4712 U[_i3] = new Array(opts.k);
4713 }
4714
4715 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
4716 var total = 0;
4717
4718 for (var j = 0; j < opts.k; j++) {
4719 U[_i4][j] = Math.random();
4720 total += U[_i4][j];
4721 }
4722
4723 for (var _j = 0; _j < opts.k; _j++) {
4724 U[_i4][_j] = U[_i4][_j] / total;
4725 }
4726 }
4727
4728 centroids = new Array(opts.k);
4729
4730 for (var _i5 = 0; _i5 < opts.k; _i5++) {
4731 centroids[_i5] = new Array(opts.attributes.length);
4732 }
4733
4734 weight = new Array(nodes.length);
4735
4736 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
4737 // N x C matrix
4738 weight[_i6] = new Array(opts.k);
4739 } // end init FCM
4740
4741
4742 var isStillMoving = true;
4743 var iterations = 0;
4744
4745 while (isStillMoving && iterations < opts.maxIterations) {
4746 isStillMoving = false; // Step 2: Calculate the centroids for each step.
4747
4748 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
4749
4750 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
4751
4752 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
4753 isStillMoving = true;
4754 }
4755
4756 iterations++;
4757 } // Assign nodes to clusters with highest probability.
4758
4759
4760 clusters = assign$1(nodes, U, opts, cy);
4761 return {
4762 clusters: clusters,
4763 degreeOfMembership: U
4764 };
4765};
4766
4767var kClustering = {
4768 kMeans: kMeans,
4769 kMedoids: kMedoids,
4770 fuzzyCMeans: fuzzyCMeans,
4771 fcm: fuzzyCMeans
4772};
4773
4774// Implemented by Zoe Xi @zoexi for GSOC 2016
4775var defaults$6 = defaults({
4776 distance: 'euclidean',
4777 // distance metric to compare nodes
4778 linkage: 'min',
4779 // linkage criterion : how to determine the distance between clusters of nodes
4780 mode: 'threshold',
4781 // mode:'threshold' => clusters must be threshold distance apart
4782 threshold: Infinity,
4783 // the distance threshold
4784 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
4785 addDendrogram: false,
4786 // whether to add the dendrogram to the graph for viz
4787 dendrogramDepth: 0,
4788 // depth at which dendrogram branches are merged into the returned clusters
4789 attributes: [] // array of attr functions
4790
4791});
4792var linkageAliases = {
4793 'single': 'min',
4794 'complete': 'max'
4795};
4796
4797var setOptions$2 = function setOptions(options) {
4798 var opts = defaults$6(options);
4799 var preferredAlias = linkageAliases[opts.linkage];
4800
4801 if (preferredAlias != null) {
4802 opts.linkage = preferredAlias;
4803 }
4804
4805 return opts;
4806};
4807
4808var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
4809 // Find two closest clusters from cached mins
4810 var minKey = 0;
4811 var min = Infinity;
4812 var dist;
4813 var attrs = opts.attributes;
4814
4815 var getDist = function getDist(n1, n2) {
4816 return clusteringDistance(opts.distance, attrs.length, function (i) {
4817 return attrs[i](n1);
4818 }, function (i) {
4819 return attrs[i](n2);
4820 }, n1, n2);
4821 };
4822
4823 for (var i = 0; i < clusters.length; i++) {
4824 var key = clusters[i].key;
4825 var _dist = dists[key][mins[key]];
4826
4827 if (_dist < min) {
4828 minKey = key;
4829 min = _dist;
4830 }
4831 }
4832
4833 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
4834 return false;
4835 }
4836
4837 var c1 = index[minKey];
4838 var c2 = index[mins[minKey]];
4839 var merged; // Merge two closest clusters
4840
4841 if (opts.mode === 'dendrogram') {
4842 merged = {
4843 left: c1,
4844 right: c2,
4845 key: c1.key
4846 };
4847 } else {
4848 merged = {
4849 value: c1.value.concat(c2.value),
4850 key: c1.key
4851 };
4852 }
4853
4854 clusters[c1.index] = merged;
4855 clusters.splice(c2.index, 1);
4856 index[c1.key] = merged; // Update distances with new merged cluster
4857
4858 for (var _i = 0; _i < clusters.length; _i++) {
4859 var cur = clusters[_i];
4860
4861 if (c1.key === cur.key) {
4862 dist = Infinity;
4863 } else if (opts.linkage === 'min') {
4864 dist = dists[c1.key][cur.key];
4865
4866 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
4867 dist = dists[c2.key][cur.key];
4868 }
4869 } else if (opts.linkage === 'max') {
4870 dist = dists[c1.key][cur.key];
4871
4872 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
4873 dist = dists[c2.key][cur.key];
4874 }
4875 } else if (opts.linkage === 'mean') {
4876 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
4877 } else {
4878 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
4879 }
4880
4881 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
4882 } // Update cached mins
4883
4884
4885 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
4886 var key1 = clusters[_i2].key;
4887
4888 if (mins[key1] === c1.key || mins[key1] === c2.key) {
4889 var _min = key1;
4890
4891 for (var j = 0; j < clusters.length; j++) {
4892 var key2 = clusters[j].key;
4893
4894 if (dists[key1][key2] < dists[key1][_min]) {
4895 _min = key2;
4896 }
4897 }
4898
4899 mins[key1] = _min;
4900 }
4901
4902 clusters[_i2].index = _i2;
4903 } // Clean up meta data used for clustering
4904
4905
4906 c1.key = c2.key = c1.index = c2.index = null;
4907 return true;
4908};
4909
4910var getAllChildren = function getAllChildren(root, arr, cy) {
4911 if (!root) return;
4912
4913 if (root.value) {
4914 arr.push(root.value);
4915 } else {
4916 if (root.left) getAllChildren(root.left, arr);
4917 if (root.right) getAllChildren(root.right, arr);
4918 }
4919};
4920
4921var buildDendrogram = function buildDendrogram(root, cy) {
4922 if (!root) return '';
4923
4924 if (root.left && root.right) {
4925 var leftStr = buildDendrogram(root.left, cy);
4926 var rightStr = buildDendrogram(root.right, cy);
4927 var node = cy.add({
4928 group: 'nodes',
4929 data: {
4930 id: leftStr + ',' + rightStr
4931 }
4932 });
4933 cy.add({
4934 group: 'edges',
4935 data: {
4936 source: leftStr,
4937 target: node.id()
4938 }
4939 });
4940 cy.add({
4941 group: 'edges',
4942 data: {
4943 source: rightStr,
4944 target: node.id()
4945 }
4946 });
4947 return node.id();
4948 } else if (root.value) {
4949 return root.value.id();
4950 }
4951};
4952
4953var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
4954 if (!root) return [];
4955 var left = [],
4956 right = [],
4957 leaves = [];
4958
4959 if (k === 0) {
4960 // don't cut tree, simply return all nodes as 1 single cluster
4961 if (root.left) getAllChildren(root.left, left);
4962 if (root.right) getAllChildren(root.right, right);
4963 leaves = left.concat(right);
4964 return [cy.collection(leaves)];
4965 } else if (k === 1) {
4966 // cut at root
4967 if (root.value) {
4968 // leaf node
4969 return [cy.collection(root.value)];
4970 } else {
4971 if (root.left) getAllChildren(root.left, left);
4972 if (root.right) getAllChildren(root.right, right);
4973 return [cy.collection(left), cy.collection(right)];
4974 }
4975 } else {
4976 if (root.value) {
4977 return [cy.collection(root.value)];
4978 } else {
4979 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
4980 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
4981 return left.concat(right);
4982 }
4983 }
4984};
4985/* eslint-enable */
4986
4987
4988var hierarchicalClustering = function hierarchicalClustering(options) {
4989 var cy = this.cy();
4990 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
4991
4992 var opts = setOptions$2(options);
4993 var attrs = opts.attributes;
4994
4995 var getDist = function getDist(n1, n2) {
4996 return clusteringDistance(opts.distance, attrs.length, function (i) {
4997 return attrs[i](n1);
4998 }, function (i) {
4999 return attrs[i](n2);
5000 }, n1, n2);
5001 }; // Begin hierarchical algorithm
5002
5003
5004 var clusters = [];
5005 var dists = []; // distances between each pair of clusters
5006
5007 var mins = []; // closest cluster for each cluster
5008
5009 var index = []; // hash of all clusters by key
5010 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5011
5012 for (var n = 0; n < nodes.length; n++) {
5013 var cluster = {
5014 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5015 key: n,
5016 index: n
5017 };
5018 clusters[n] = cluster;
5019 index[n] = cluster;
5020 dists[n] = [];
5021 mins[n] = 0;
5022 } // Calculate the distance between each pair of clusters
5023
5024
5025 for (var i = 0; i < clusters.length; i++) {
5026 for (var j = 0; j <= i; j++) {
5027 var dist = void 0;
5028
5029 if (opts.mode === 'dendrogram') {
5030 // modes store cluster values differently
5031 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5032 } else {
5033 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5034 }
5035
5036 dists[i][j] = dist;
5037 dists[j][i] = dist;
5038
5039 if (dist < dists[i][mins[i]]) {
5040 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5041 }
5042 }
5043 } // Find the closest pair of clusters and merge them into a single cluster.
5044 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5045
5046
5047 var merged = mergeClosest(clusters, index, dists, mins, opts);
5048
5049 while (merged) {
5050 merged = mergeClosest(clusters, index, dists, mins, opts);
5051 }
5052
5053 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5054 // in addition to returning the clusters.
5055
5056 if (opts.mode === 'dendrogram') {
5057 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5058 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5059 } else {
5060 // Regular mode simply returns the clusters
5061 retClusters = new Array(clusters.length);
5062 clusters.forEach(function (cluster, i) {
5063 // Clean up meta data used for clustering
5064 cluster.key = cluster.index = null;
5065 retClusters[i] = cy.collection(cluster.value);
5066 });
5067 }
5068
5069 return retClusters;
5070};
5071
5072var hierarchicalClustering$1 = {
5073 hierarchicalClustering: hierarchicalClustering,
5074 hca: hierarchicalClustering
5075};
5076
5077// Implemented by Zoe Xi @zoexi for GSOC 2016
5078var defaults$7 = defaults({
5079 distance: 'euclidean',
5080 // distance metric to compare attributes between two nodes
5081 preference: 'median',
5082 // suitability of a data point to serve as an exemplar
5083 damping: 0.8,
5084 // damping factor between [0.5, 1)
5085 maxIterations: 1000,
5086 // max number of iterations to run
5087 minIterations: 100,
5088 // min number of iterations to run in order for clustering to stop
5089 attributes: [// functions to quantify the similarity between any two points
5090 // e.g. node => node.data('weight')
5091 ]
5092});
5093
5094var setOptions$3 = function setOptions(options) {
5095 var dmp = options.damping;
5096 var pref = options.preference;
5097
5098 if (!(0.5 <= dmp && dmp < 1)) {
5099 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5100 }
5101
5102 var validPrefs = ['median', 'mean', 'min', 'max'];
5103
5104 if (!(validPrefs.some(function (v) {
5105 return v === pref;
5106 }) || number(pref))) {
5107 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5108 return "'".concat(p, "'");
5109 }).join(', '), "] or a number. Got: ").concat(pref));
5110 }
5111
5112 return defaults$7(options);
5113};
5114/* eslint-enable */
5115
5116
5117var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5118 var attr = function attr(n, i) {
5119 return attributes[i](n);
5120 }; // nb negative because similarity should have an inverse relationship to distance
5121
5122
5123 return -clusteringDistance(type, attributes.length, function (i) {
5124 return attr(n1, i);
5125 }, function (i) {
5126 return attr(n2, i);
5127 }, n1, n2);
5128};
5129
5130var getPreference = function getPreference(S, preference) {
5131 // larger preference = greater # of clusters
5132 var p = null;
5133
5134 if (preference === 'median') {
5135 p = median(S);
5136 } else if (preference === 'mean') {
5137 p = mean(S);
5138 } else if (preference === 'min') {
5139 p = min(S);
5140 } else if (preference === 'max') {
5141 p = max(S);
5142 } else {
5143 // Custom preference number, as set by user
5144 p = preference;
5145 }
5146
5147 return p;
5148};
5149
5150var findExemplars = function findExemplars(n, R, A) {
5151 var indices = [];
5152
5153 for (var i = 0; i < n; i++) {
5154 if (R[i * n + i] + A[i * n + i] > 0) {
5155 indices.push(i);
5156 }
5157 }
5158
5159 return indices;
5160};
5161
5162var assignClusters = function assignClusters(n, S, exemplars) {
5163 var clusters = [];
5164
5165 for (var i = 0; i < n; i++) {
5166 var index = -1;
5167 var max = -Infinity;
5168
5169 for (var ei = 0; ei < exemplars.length; ei++) {
5170 var e = exemplars[ei];
5171
5172 if (S[i * n + e] > max) {
5173 index = e;
5174 max = S[i * n + e];
5175 }
5176 }
5177
5178 if (index > 0) {
5179 clusters.push(index);
5180 }
5181 }
5182
5183 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5184 clusters[exemplars[_ei]] = exemplars[_ei];
5185 }
5186
5187 return clusters;
5188};
5189
5190var assign$2 = function assign(n, S, exemplars) {
5191 var clusters = assignClusters(n, S, exemplars);
5192
5193 for (var ei = 0; ei < exemplars.length; ei++) {
5194 var ii = [];
5195
5196 for (var c = 0; c < clusters.length; c++) {
5197 if (clusters[c] === exemplars[ei]) {
5198 ii.push(c);
5199 }
5200 }
5201
5202 var maxI = -1;
5203 var maxSum = -Infinity;
5204
5205 for (var i = 0; i < ii.length; i++) {
5206 var sum = 0;
5207
5208 for (var j = 0; j < ii.length; j++) {
5209 sum += S[ii[j] * n + ii[i]];
5210 }
5211
5212 if (sum > maxSum) {
5213 maxI = i;
5214 maxSum = sum;
5215 }
5216 }
5217
5218 exemplars[ei] = ii[maxI];
5219 }
5220
5221 clusters = assignClusters(n, S, exemplars);
5222 return clusters;
5223};
5224
5225var affinityPropagation = function affinityPropagation(options) {
5226 var cy = this.cy();
5227 var nodes = this.nodes();
5228 var opts = setOptions$3(options); // Map each node to its position in node array
5229
5230 var id2position = {};
5231
5232 for (var i = 0; i < nodes.length; i++) {
5233 id2position[nodes[i].id()] = i;
5234 } // Begin affinity propagation algorithm
5235
5236
5237 var n; // number of data points
5238
5239 var n2; // size of matrices
5240
5241 var S; // similarity matrix (1D array)
5242
5243 var p; // preference/suitability of a data point to serve as an exemplar
5244
5245 var R; // responsibility matrix (1D array)
5246
5247 var A; // availability matrix (1D array)
5248
5249 n = nodes.length;
5250 n2 = n * n; // Initialize and build S similarity matrix
5251
5252 S = new Array(n2);
5253
5254 for (var _i = 0; _i < n2; _i++) {
5255 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5256 }
5257
5258 for (var _i2 = 0; _i2 < n; _i2++) {
5259 for (var j = 0; j < n; j++) {
5260 if (_i2 !== j) {
5261 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5262 }
5263 }
5264 } // Place preferences on the diagonal of S
5265
5266
5267 p = getPreference(S, opts.preference);
5268
5269 for (var _i3 = 0; _i3 < n; _i3++) {
5270 S[_i3 * n + _i3] = p;
5271 } // Initialize R responsibility matrix
5272
5273
5274 R = new Array(n2);
5275
5276 for (var _i4 = 0; _i4 < n2; _i4++) {
5277 R[_i4] = 0.0;
5278 } // Initialize A availability matrix
5279
5280
5281 A = new Array(n2);
5282
5283 for (var _i5 = 0; _i5 < n2; _i5++) {
5284 A[_i5] = 0.0;
5285 }
5286
5287 var old = new Array(n);
5288 var Rp = new Array(n);
5289 var se = new Array(n);
5290
5291 for (var _i6 = 0; _i6 < n; _i6++) {
5292 old[_i6] = 0.0;
5293 Rp[_i6] = 0.0;
5294 se[_i6] = 0;
5295 }
5296
5297 var e = new Array(n * opts.minIterations);
5298
5299 for (var _i7 = 0; _i7 < e.length; _i7++) {
5300 e[_i7] = 0;
5301 }
5302
5303 var iter;
5304
5305 for (iter = 0; iter < opts.maxIterations; iter++) {
5306 // main algorithmic loop
5307 // Update R responsibility matrix
5308 for (var _i8 = 0; _i8 < n; _i8++) {
5309 var max = -Infinity,
5310 max2 = -Infinity,
5311 maxI = -1,
5312 AS = 0.0;
5313
5314 for (var _j = 0; _j < n; _j++) {
5315 old[_j] = R[_i8 * n + _j];
5316 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5317
5318 if (AS >= max) {
5319 max2 = max;
5320 max = AS;
5321 maxI = _j;
5322 } else if (AS > max2) {
5323 max2 = AS;
5324 }
5325 }
5326
5327 for (var _j2 = 0; _j2 < n; _j2++) {
5328 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5329 }
5330
5331 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5332 } // Update A availability matrix
5333
5334
5335 for (var _i9 = 0; _i9 < n; _i9++) {
5336 var sum = 0;
5337
5338 for (var _j3 = 0; _j3 < n; _j3++) {
5339 old[_j3] = A[_j3 * n + _i9];
5340 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5341 sum += Rp[_j3];
5342 }
5343
5344 sum -= Rp[_i9];
5345 Rp[_i9] = R[_i9 * n + _i9];
5346 sum += Rp[_i9];
5347
5348 for (var _j4 = 0; _j4 < n; _j4++) {
5349 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5350 }
5351
5352 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5353 } // Check for convergence
5354
5355
5356 var K = 0;
5357
5358 for (var _i10 = 0; _i10 < n; _i10++) {
5359 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5360 e[iter % opts.minIterations * n + _i10] = E;
5361 K += E;
5362 }
5363
5364 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5365 var _sum = 0;
5366
5367 for (var _i11 = 0; _i11 < n; _i11++) {
5368 se[_i11] = 0;
5369
5370 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5371 se[_i11] += e[_j5 * n + _i11];
5372 }
5373
5374 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5375 _sum++;
5376 }
5377 }
5378
5379 if (_sum === n) {
5380 // then we have convergence
5381 break;
5382 }
5383 }
5384 } // Identify exemplars (cluster centers)
5385
5386
5387 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
5388
5389 var clusterIndices = assign$2(n, S, exemplarsIndices);
5390 var clusters = {};
5391
5392 for (var c = 0; c < exemplarsIndices.length; c++) {
5393 clusters[exemplarsIndices[c]] = [];
5394 }
5395
5396 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5397 var pos = id2position[nodes[_i12].id()];
5398
5399 var clusterIndex = clusterIndices[pos];
5400
5401 if (clusterIndex != null) {
5402 // the node may have not been assigned a cluster if no valid attributes were specified
5403 clusters[clusterIndex].push(nodes[_i12]);
5404 }
5405 }
5406
5407 var retClusters = new Array(exemplarsIndices.length);
5408
5409 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5410 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5411 }
5412
5413 return retClusters;
5414};
5415
5416var affinityPropagation$1 = {
5417 affinityPropagation: affinityPropagation,
5418 ap: affinityPropagation
5419};
5420
5421var hierholzerDefaults = defaults({
5422 root: undefined,
5423 directed: false
5424});
5425var elesfn$b = {
5426 hierholzer: function hierholzer(options) {
5427 if (!plainObject(options)) {
5428 var args = arguments;
5429 options = {
5430 root: args[0],
5431 directed: args[1]
5432 };
5433 }
5434
5435 var _hierholzerDefaults = hierholzerDefaults(options),
5436 root = _hierholzerDefaults.root,
5437 directed = _hierholzerDefaults.directed;
5438
5439 var eles = this;
5440 var dflag = false;
5441 var oddIn;
5442 var oddOut;
5443 var startVertex;
5444 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5445 var nodes = {};
5446 var edges = {};
5447
5448 if (directed) {
5449 eles.forEach(function (ele) {
5450 var id = ele.id();
5451
5452 if (ele.isNode()) {
5453 var ind = ele.indegree(true);
5454 var outd = ele.outdegree(true);
5455 var d1 = ind - outd;
5456 var d2 = outd - ind;
5457
5458 if (d1 == 1) {
5459 if (oddIn) dflag = true;else oddIn = id;
5460 } else if (d2 == 1) {
5461 if (oddOut) dflag = true;else oddOut = id;
5462 } else if (d2 > 1 || d1 > 1) {
5463 dflag = true;
5464 }
5465
5466 nodes[id] = [];
5467 ele.outgoers().forEach(function (e) {
5468 if (e.isEdge()) nodes[id].push(e.id());
5469 });
5470 } else {
5471 edges[id] = [undefined, ele.target().id()];
5472 }
5473 });
5474 } else {
5475 eles.forEach(function (ele) {
5476 var id = ele.id();
5477
5478 if (ele.isNode()) {
5479 var d = ele.degree(true);
5480
5481 if (d % 2) {
5482 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5483 }
5484
5485 nodes[id] = [];
5486 ele.connectedEdges().forEach(function (e) {
5487 return nodes[id].push(e.id());
5488 });
5489 } else {
5490 edges[id] = [ele.source().id(), ele.target().id()];
5491 }
5492 });
5493 }
5494
5495 var result = {
5496 found: false,
5497 trail: undefined
5498 };
5499 if (dflag) return result;else if (oddOut && oddIn) {
5500 if (directed) {
5501 if (startVertex && oddOut != startVertex) {
5502 return result;
5503 }
5504
5505 startVertex = oddOut;
5506 } else {
5507 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5508 return result;
5509 } else if (!startVertex) {
5510 startVertex = oddOut;
5511 }
5512 }
5513 } else {
5514 if (!startVertex) startVertex = eles[0].id();
5515 }
5516
5517 var walk = function walk(v) {
5518 var currentNode = v;
5519 var subtour = [v];
5520 var adj, adjTail, adjHead;
5521
5522 while (nodes[currentNode].length) {
5523 adj = nodes[currentNode].shift();
5524 adjTail = edges[adj][0];
5525 adjHead = edges[adj][1];
5526
5527 if (currentNode != adjHead) {
5528 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5529 return e != adj;
5530 });
5531 currentNode = adjHead;
5532 } else if (!directed && currentNode != adjTail) {
5533 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5534 return e != adj;
5535 });
5536 currentNode = adjTail;
5537 }
5538
5539 subtour.unshift(adj);
5540 subtour.unshift(currentNode);
5541 }
5542
5543 return subtour;
5544 };
5545
5546 var trail = [];
5547 var subtour = [];
5548 subtour = walk(startVertex);
5549
5550 while (subtour.length != 1) {
5551 if (nodes[subtour[0]].length == 0) {
5552 trail.unshift(eles.getElementById(subtour.shift()));
5553 trail.unshift(eles.getElementById(subtour.shift()));
5554 } else {
5555 subtour = walk(subtour.shift()).concat(subtour);
5556 }
5557 }
5558
5559 trail.unshift(eles.getElementById(subtour.shift())); // final node
5560
5561 for (var d in nodes) {
5562 if (nodes[d].length) {
5563 return result;
5564 }
5565 }
5566
5567 result.found = true;
5568 result.trail = this.spawn(trail);
5569 return result;
5570 }
5571};
5572
5573var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5574 var eles = this;
5575 var nodes = {};
5576 var id = 0;
5577 var edgeCount = 0;
5578 var components = [];
5579 var stack = [];
5580 var visitedEdges = {};
5581
5582 var buildComponent = function buildComponent(x, y) {
5583 var i = stack.length - 1;
5584 var cutset = [];
5585 var component = eles.spawn();
5586
5587 while (stack[i].x != x || stack[i].y != y) {
5588 cutset.push(stack.pop().edge);
5589 i--;
5590 }
5591
5592 cutset.push(stack.pop().edge);
5593 cutset.forEach(function (edge) {
5594 var connectedNodes = edge.connectedNodes().intersection(eles);
5595 component.merge(edge);
5596 connectedNodes.forEach(function (node) {
5597 var nodeId = node.id();
5598 var connectedEdges = node.connectedEdges().intersection(eles);
5599 component.merge(node);
5600
5601 if (!nodes[nodeId].cutVertex) {
5602 component.merge(connectedEdges);
5603 } else {
5604 component.merge(connectedEdges.filter(function (edge) {
5605 return edge.isLoop();
5606 }));
5607 }
5608 });
5609 });
5610 components.push(component);
5611 };
5612
5613 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5614 if (root === parent) edgeCount += 1;
5615 nodes[currentNode] = {
5616 id: id,
5617 low: id++,
5618 cutVertex: false
5619 };
5620 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5621
5622 if (edges.size() === 0) {
5623 components.push(eles.spawn(eles.getElementById(currentNode)));
5624 } else {
5625 var sourceId, targetId, otherNodeId, edgeId;
5626 edges.forEach(function (edge) {
5627 sourceId = edge.source().id();
5628 targetId = edge.target().id();
5629 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5630
5631 if (otherNodeId !== parent) {
5632 edgeId = edge.id();
5633
5634 if (!visitedEdges[edgeId]) {
5635 visitedEdges[edgeId] = true;
5636 stack.push({
5637 x: currentNode,
5638 y: otherNodeId,
5639 edge: edge
5640 });
5641 }
5642
5643 if (!(otherNodeId in nodes)) {
5644 biconnectedSearch(root, otherNodeId, currentNode);
5645 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
5646
5647 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
5648 nodes[currentNode].cutVertex = true;
5649 buildComponent(currentNode, otherNodeId);
5650 }
5651 } else {
5652 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
5653 }
5654 }
5655 });
5656 }
5657 };
5658
5659 eles.forEach(function (ele) {
5660 if (ele.isNode()) {
5661 var nodeId = ele.id();
5662
5663 if (!(nodeId in nodes)) {
5664 edgeCount = 0;
5665 biconnectedSearch(nodeId, nodeId);
5666 nodes[nodeId].cutVertex = edgeCount > 1;
5667 }
5668 }
5669 });
5670 var cutVertices = Object.keys(nodes).filter(function (id) {
5671 return nodes[id].cutVertex;
5672 }).map(function (id) {
5673 return eles.getElementById(id);
5674 });
5675 return {
5676 cut: eles.spawn(cutVertices),
5677 components: components
5678 };
5679};
5680
5681var hopcroftTarjanBiconnected$1 = {
5682 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
5683 htbc: hopcroftTarjanBiconnected,
5684 htb: hopcroftTarjanBiconnected,
5685 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
5686};
5687
5688var tarjanStronglyConnected = function tarjanStronglyConnected() {
5689 var eles = this;
5690 var nodes = {};
5691 var index = 0;
5692 var components = [];
5693 var stack = [];
5694 var cut = eles.spawn(eles);
5695
5696 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
5697 stack.push(sourceNodeId);
5698 nodes[sourceNodeId] = {
5699 index: index,
5700 low: index++,
5701 explored: false
5702 };
5703 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
5704 connectedEdges.forEach(function (edge) {
5705 var targetNodeId = edge.target().id();
5706
5707 if (targetNodeId !== sourceNodeId) {
5708 if (!(targetNodeId in nodes)) {
5709 stronglyConnectedSearch(targetNodeId);
5710 }
5711
5712 if (!nodes[targetNodeId].explored) {
5713 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
5714 }
5715 }
5716 });
5717
5718 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
5719 var componentNodes = eles.spawn();
5720
5721 for (;;) {
5722 var nodeId = stack.pop();
5723 componentNodes.merge(eles.getElementById(nodeId));
5724 nodes[nodeId].low = nodes[sourceNodeId].index;
5725 nodes[nodeId].explored = true;
5726
5727 if (nodeId === sourceNodeId) {
5728 break;
5729 }
5730 }
5731
5732 var componentEdges = componentNodes.edgesWith(componentNodes);
5733 var component = componentNodes.merge(componentEdges);
5734 components.push(component);
5735 cut = cut.difference(component);
5736 }
5737 };
5738
5739 eles.forEach(function (ele) {
5740 if (ele.isNode()) {
5741 var nodeId = ele.id();
5742
5743 if (!(nodeId in nodes)) {
5744 stronglyConnectedSearch(nodeId);
5745 }
5746 }
5747 });
5748 return {
5749 cut: cut,
5750 components: components
5751 };
5752};
5753
5754var tarjanStronglyConnected$1 = {
5755 tarjanStronglyConnected: tarjanStronglyConnected,
5756 tsc: tarjanStronglyConnected,
5757 tscc: tarjanStronglyConnected,
5758 tarjanStronglyConnectedComponents: tarjanStronglyConnected
5759};
5760
5761var elesfn$c = {};
5762[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) {
5763 extend(elesfn$c, props);
5764});
5765
5766/*!
5767Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
5768Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
5769Licensed under The MIT License (http://opensource.org/licenses/MIT)
5770*/
5771
5772/* promise states [Promises/A+ 2.1] */
5773var STATE_PENDING = 0;
5774/* [Promises/A+ 2.1.1] */
5775
5776var STATE_FULFILLED = 1;
5777/* [Promises/A+ 2.1.2] */
5778
5779var STATE_REJECTED = 2;
5780/* [Promises/A+ 2.1.3] */
5781
5782/* promise object constructor */
5783
5784var api = function api(executor) {
5785 /* optionally support non-constructor/plain-function call */
5786 if (!(this instanceof api)) return new api(executor);
5787 /* initialize object */
5788
5789 this.id = 'Thenable/1.0.7';
5790 this.state = STATE_PENDING;
5791 /* initial state */
5792
5793 this.fulfillValue = undefined;
5794 /* initial value */
5795
5796 /* [Promises/A+ 1.3, 2.1.2.2] */
5797
5798 this.rejectReason = undefined;
5799 /* initial reason */
5800
5801 /* [Promises/A+ 1.5, 2.1.3.2] */
5802
5803 this.onFulfilled = [];
5804 /* initial handlers */
5805
5806 this.onRejected = [];
5807 /* initial handlers */
5808
5809 /* provide optional information-hiding proxy */
5810
5811 this.proxy = {
5812 then: this.then.bind(this)
5813 };
5814 /* support optional executor function */
5815
5816 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
5817};
5818/* promise API methods */
5819
5820
5821api.prototype = {
5822 /* promise resolving methods */
5823 fulfill: function fulfill(value) {
5824 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
5825 },
5826 reject: function reject(value) {
5827 return deliver(this, STATE_REJECTED, 'rejectReason', value);
5828 },
5829
5830 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
5831 then: function then(onFulfilled, onRejected) {
5832 var curr = this;
5833 var next = new api();
5834 /* [Promises/A+ 2.2.7] */
5835
5836 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
5837 /* [Promises/A+ 2.2.2/2.2.6] */
5838
5839 curr.onRejected.push(resolver(onRejected, next, 'reject'));
5840 /* [Promises/A+ 2.2.3/2.2.6] */
5841
5842 execute(curr);
5843 return next.proxy;
5844 /* [Promises/A+ 2.2.7, 3.3] */
5845 }
5846};
5847/* deliver an action */
5848
5849var deliver = function deliver(curr, state, name, value) {
5850 if (curr.state === STATE_PENDING) {
5851 curr.state = state;
5852 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
5853
5854 curr[name] = value;
5855 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
5856
5857 execute(curr);
5858 }
5859
5860 return curr;
5861};
5862/* execute all handlers */
5863
5864
5865var execute = function execute(curr) {
5866 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
5867};
5868/* execute particular set of handlers */
5869
5870
5871var execute_handlers = function execute_handlers(curr, name, value) {
5872 /* global setImmediate: true */
5873
5874 /* global setTimeout: true */
5875
5876 /* short-circuit processing */
5877 if (curr[name].length === 0) return;
5878 /* iterate over all handlers, exactly once */
5879
5880 var handlers = curr[name];
5881 curr[name] = [];
5882 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
5883
5884 var func = function func() {
5885 for (var i = 0; i < handlers.length; i++) {
5886 handlers[i](value);
5887 }
5888 /* [Promises/A+ 2.2.5] */
5889
5890 };
5891 /* execute procedure asynchronously */
5892
5893 /* [Promises/A+ 2.2.4, 3.1] */
5894
5895
5896 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
5897};
5898/* generate a resolver function */
5899
5900
5901var resolver = function resolver(cb, next, method) {
5902 return function (value) {
5903 if (typeof cb !== 'function')
5904 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
5905 next[method].call(next, value);
5906 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
5907 else {
5908 var result;
5909
5910 try {
5911 result = cb(value);
5912 }
5913 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
5914 catch (e) {
5915 next.reject(e);
5916 /* [Promises/A+ 2.2.7.2] */
5917
5918 return;
5919 }
5920
5921 resolve(next, result);
5922 /* [Promises/A+ 2.2.7.1] */
5923 }
5924 };
5925};
5926/* "Promise Resolution Procedure" */
5927
5928/* [Promises/A+ 2.3] */
5929
5930
5931var resolve = function resolve(promise, x) {
5932 /* sanity check arguments */
5933
5934 /* [Promises/A+ 2.3.1] */
5935 if (promise === x || promise.proxy === x) {
5936 promise.reject(new TypeError('cannot resolve promise with itself'));
5937 return;
5938 }
5939 /* surgically check for a "then" method
5940 (mainly to just call the "getter" of "then" only once) */
5941
5942
5943 var then;
5944
5945 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
5946 try {
5947 then = x.then;
5948 }
5949 /* [Promises/A+ 2.3.3.1, 3.5] */
5950 catch (e) {
5951 promise.reject(e);
5952 /* [Promises/A+ 2.3.3.2] */
5953
5954 return;
5955 }
5956 }
5957 /* handle own Thenables [Promises/A+ 2.3.2]
5958 and similar "thenables" [Promises/A+ 2.3.3] */
5959
5960
5961 if (typeof then === 'function') {
5962 var resolved = false;
5963
5964 try {
5965 /* call retrieved "then" method */
5966
5967 /* [Promises/A+ 2.3.3.3] */
5968 then.call(x,
5969 /* resolvePromise */
5970
5971 /* [Promises/A+ 2.3.3.3.1] */
5972 function (y) {
5973 if (resolved) return;
5974 resolved = true;
5975 /* [Promises/A+ 2.3.3.3.3] */
5976
5977 if (y === x)
5978 /* [Promises/A+ 3.6] */
5979 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
5980 },
5981 /* rejectPromise */
5982
5983 /* [Promises/A+ 2.3.3.3.2] */
5984 function (r) {
5985 if (resolved) return;
5986 resolved = true;
5987 /* [Promises/A+ 2.3.3.3.3] */
5988
5989 promise.reject(r);
5990 });
5991 } catch (e) {
5992 if (!resolved)
5993 /* [Promises/A+ 2.3.3.3.3] */
5994 promise.reject(e);
5995 /* [Promises/A+ 2.3.3.3.4] */
5996 }
5997
5998 return;
5999 }
6000 /* handle other values */
6001
6002
6003 promise.fulfill(x);
6004 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6005}; // so we always have Promise.all()
6006
6007
6008api.all = function (ps) {
6009 return new api(function (resolveAll, rejectAll) {
6010 var vals = new Array(ps.length);
6011 var doneCount = 0;
6012
6013 var fulfill = function fulfill(i, val) {
6014 vals[i] = val;
6015 doneCount++;
6016
6017 if (doneCount === ps.length) {
6018 resolveAll(vals);
6019 }
6020 };
6021
6022 for (var i = 0; i < ps.length; i++) {
6023 (function (i) {
6024 var p = ps[i];
6025 var isPromise = p != null && p.then != null;
6026
6027 if (isPromise) {
6028 p.then(function (val) {
6029 fulfill(i, val);
6030 }, function (err) {
6031 rejectAll(err);
6032 });
6033 } else {
6034 var val = p;
6035 fulfill(i, val);
6036 }
6037 })(i);
6038 }
6039 });
6040};
6041
6042api.resolve = function (val) {
6043 return new api(function (resolve, reject) {
6044 resolve(val);
6045 });
6046};
6047
6048api.reject = function (val) {
6049 return new api(function (resolve, reject) {
6050 reject(val);
6051 });
6052};
6053
6054var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6055
6056var Animation = function Animation(target, opts, opts2) {
6057 var isCore = core(target);
6058 var isEle = !isCore;
6059
6060 var _p = this._private = extend({
6061 duration: 1000
6062 }, opts, opts2);
6063
6064 _p.target = target;
6065 _p.style = _p.style || _p.css;
6066 _p.started = false;
6067 _p.playing = false;
6068 _p.hooked = false;
6069 _p.applying = false;
6070 _p.progress = 0;
6071 _p.completes = [];
6072 _p.frames = [];
6073
6074 if (_p.complete && fn(_p.complete)) {
6075 _p.completes.push(_p.complete);
6076 }
6077
6078 if (isEle) {
6079 var pos = target.position();
6080 _p.startPosition = _p.startPosition || {
6081 x: pos.x,
6082 y: pos.y
6083 };
6084 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6085 }
6086
6087 if (isCore) {
6088 var pan = target.pan();
6089 _p.startPan = {
6090 x: pan.x,
6091 y: pan.y
6092 };
6093 _p.startZoom = target.zoom();
6094 } // for future timeline/animations impl
6095
6096
6097 this.length = 1;
6098 this[0] = this;
6099};
6100
6101var anifn = Animation.prototype;
6102extend(anifn, {
6103 instanceString: function instanceString() {
6104 return 'animation';
6105 },
6106 hook: function hook() {
6107 var _p = this._private;
6108
6109 if (!_p.hooked) {
6110 // add to target's animation queue
6111 var q;
6112 var tAni = _p.target._private.animation;
6113
6114 if (_p.queue) {
6115 q = tAni.queue;
6116 } else {
6117 q = tAni.current;
6118 }
6119
6120 q.push(this); // add to the animation loop pool
6121
6122 if (elementOrCollection(_p.target)) {
6123 _p.target.cy().addToAnimationPool(_p.target);
6124 }
6125
6126 _p.hooked = true;
6127 }
6128
6129 return this;
6130 },
6131 play: function play() {
6132 var _p = this._private; // autorewind
6133
6134 if (_p.progress === 1) {
6135 _p.progress = 0;
6136 }
6137
6138 _p.playing = true;
6139 _p.started = false; // needs to be started by animation loop
6140
6141 _p.stopped = false;
6142 this.hook(); // the animation loop will start the animation...
6143
6144 return this;
6145 },
6146 playing: function playing() {
6147 return this._private.playing;
6148 },
6149 apply: function apply() {
6150 var _p = this._private;
6151 _p.applying = true;
6152 _p.started = false; // needs to be started by animation loop
6153
6154 _p.stopped = false;
6155 this.hook(); // the animation loop will apply the animation at this progress
6156
6157 return this;
6158 },
6159 applying: function applying() {
6160 return this._private.applying;
6161 },
6162 pause: function pause() {
6163 var _p = this._private;
6164 _p.playing = false;
6165 _p.started = false;
6166 return this;
6167 },
6168 stop: function stop() {
6169 var _p = this._private;
6170 _p.playing = false;
6171 _p.started = false;
6172 _p.stopped = true; // to be removed from animation queues
6173
6174 return this;
6175 },
6176 rewind: function rewind() {
6177 return this.progress(0);
6178 },
6179 fastforward: function fastforward() {
6180 return this.progress(1);
6181 },
6182 time: function time(t) {
6183 var _p = this._private;
6184
6185 if (t === undefined) {
6186 return _p.progress * _p.duration;
6187 } else {
6188 return this.progress(t / _p.duration);
6189 }
6190 },
6191 progress: function progress(p) {
6192 var _p = this._private;
6193 var wasPlaying = _p.playing;
6194
6195 if (p === undefined) {
6196 return _p.progress;
6197 } else {
6198 if (wasPlaying) {
6199 this.pause();
6200 }
6201
6202 _p.progress = p;
6203 _p.started = false;
6204
6205 if (wasPlaying) {
6206 this.play();
6207 }
6208 }
6209
6210 return this;
6211 },
6212 completed: function completed() {
6213 return this._private.progress === 1;
6214 },
6215 reverse: function reverse() {
6216 var _p = this._private;
6217 var wasPlaying = _p.playing;
6218
6219 if (wasPlaying) {
6220 this.pause();
6221 }
6222
6223 _p.progress = 1 - _p.progress;
6224 _p.started = false;
6225
6226 var swap = function swap(a, b) {
6227 var _pa = _p[a];
6228
6229 if (_pa == null) {
6230 return;
6231 }
6232
6233 _p[a] = _p[b];
6234 _p[b] = _pa;
6235 };
6236
6237 swap('zoom', 'startZoom');
6238 swap('pan', 'startPan');
6239 swap('position', 'startPosition'); // swap styles
6240
6241 if (_p.style) {
6242 for (var i = 0; i < _p.style.length; i++) {
6243 var prop = _p.style[i];
6244 var name = prop.name;
6245 var startStyleProp = _p.startStyle[name];
6246 _p.startStyle[name] = prop;
6247 _p.style[i] = startStyleProp;
6248 }
6249 }
6250
6251 if (wasPlaying) {
6252 this.play();
6253 }
6254
6255 return this;
6256 },
6257 promise: function promise(type) {
6258 var _p = this._private;
6259 var arr;
6260
6261 switch (type) {
6262 case 'frame':
6263 arr = _p.frames;
6264 break;
6265
6266 default:
6267 case 'complete':
6268 case 'completed':
6269 arr = _p.completes;
6270 }
6271
6272 return new Promise$1(function (resolve, reject) {
6273 arr.push(function () {
6274 resolve();
6275 });
6276 });
6277 }
6278});
6279anifn.complete = anifn.completed;
6280anifn.run = anifn.play;
6281anifn.running = anifn.playing;
6282
6283var define = {
6284 animated: function animated() {
6285 return function animatedImpl() {
6286 var self = this;
6287 var selfIsArrayLike = self.length !== undefined;
6288 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6289
6290 var cy = this._private.cy || this;
6291
6292 if (!cy.styleEnabled()) {
6293 return false;
6294 }
6295
6296 var ele = all[0];
6297
6298 if (ele) {
6299 return ele._private.animation.current.length > 0;
6300 }
6301 };
6302 },
6303 // animated
6304 clearQueue: function clearQueue() {
6305 return function clearQueueImpl() {
6306 var self = this;
6307 var selfIsArrayLike = self.length !== undefined;
6308 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6309
6310 var cy = this._private.cy || this;
6311
6312 if (!cy.styleEnabled()) {
6313 return this;
6314 }
6315
6316 for (var i = 0; i < all.length; i++) {
6317 var ele = all[i];
6318 ele._private.animation.queue = [];
6319 }
6320
6321 return this;
6322 };
6323 },
6324 // clearQueue
6325 delay: function delay() {
6326 return function delayImpl(time, complete) {
6327 var cy = this._private.cy || this;
6328
6329 if (!cy.styleEnabled()) {
6330 return this;
6331 }
6332
6333 return this.animate({
6334 delay: time,
6335 duration: time,
6336 complete: complete
6337 });
6338 };
6339 },
6340 // delay
6341 delayAnimation: function delayAnimation() {
6342 return function delayAnimationImpl(time, complete) {
6343 var cy = this._private.cy || this;
6344
6345 if (!cy.styleEnabled()) {
6346 return this;
6347 }
6348
6349 return this.animation({
6350 delay: time,
6351 duration: time,
6352 complete: complete
6353 });
6354 };
6355 },
6356 // delay
6357 animation: function animation() {
6358 return function animationImpl(properties, params) {
6359 var self = this;
6360 var selfIsArrayLike = self.length !== undefined;
6361 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6362
6363 var cy = this._private.cy || this;
6364 var isCore = !selfIsArrayLike;
6365 var isEles = !isCore;
6366
6367 if (!cy.styleEnabled()) {
6368 return this;
6369 }
6370
6371 var style = cy.style();
6372 properties = extend({}, properties, params);
6373 var propertiesEmpty = Object.keys(properties).length === 0;
6374
6375 if (propertiesEmpty) {
6376 return new Animation(all[0], properties); // nothing to animate
6377 }
6378
6379 if (properties.duration === undefined) {
6380 properties.duration = 400;
6381 }
6382
6383 switch (properties.duration) {
6384 case 'slow':
6385 properties.duration = 600;
6386 break;
6387
6388 case 'fast':
6389 properties.duration = 200;
6390 break;
6391 }
6392
6393 if (isEles) {
6394 properties.style = style.getPropsList(properties.style || properties.css);
6395 properties.css = undefined;
6396 }
6397
6398 if (isEles && properties.renderedPosition != null) {
6399 var rpos = properties.renderedPosition;
6400 var pan = cy.pan();
6401 var zoom = cy.zoom();
6402 properties.position = renderedToModelPosition(rpos, zoom, pan);
6403 } // override pan w/ panBy if set
6404
6405
6406 if (isCore && properties.panBy != null) {
6407 var panBy = properties.panBy;
6408 var cyPan = cy.pan();
6409 properties.pan = {
6410 x: cyPan.x + panBy.x,
6411 y: cyPan.y + panBy.y
6412 };
6413 } // override pan w/ center if set
6414
6415
6416 var center = properties.center || properties.centre;
6417
6418 if (isCore && center != null) {
6419 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6420
6421 if (centerPan != null) {
6422 properties.pan = centerPan;
6423 }
6424 } // override pan & zoom w/ fit if set
6425
6426
6427 if (isCore && properties.fit != null) {
6428 var fit = properties.fit;
6429 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6430
6431 if (fitVp != null) {
6432 properties.pan = fitVp.pan;
6433 properties.zoom = fitVp.zoom;
6434 }
6435 } // override zoom (& potentially pan) w/ zoom obj if set
6436
6437
6438 if (isCore && plainObject(properties.zoom)) {
6439 var vp = cy.getZoomedViewport(properties.zoom);
6440
6441 if (vp != null) {
6442 if (vp.zoomed) {
6443 properties.zoom = vp.zoom;
6444 }
6445
6446 if (vp.panned) {
6447 properties.pan = vp.pan;
6448 }
6449 } else {
6450 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6451 }
6452 }
6453
6454 return new Animation(all[0], properties);
6455 };
6456 },
6457 // animate
6458 animate: function animate() {
6459 return function animateImpl(properties, params) {
6460 var self = this;
6461 var selfIsArrayLike = self.length !== undefined;
6462 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6463
6464 var cy = this._private.cy || this;
6465
6466 if (!cy.styleEnabled()) {
6467 return this;
6468 }
6469
6470 if (params) {
6471 properties = extend({}, properties, params);
6472 } // manually hook and run the animation
6473
6474
6475 for (var i = 0; i < all.length; i++) {
6476 var ele = all[i];
6477 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6478 var ani = ele.animation(properties, queue ? {
6479 queue: true
6480 } : undefined);
6481 ani.play();
6482 }
6483
6484 return this; // chaining
6485 };
6486 },
6487 // animate
6488 stop: function stop() {
6489 return function stopImpl(clearQueue, jumpToEnd) {
6490 var self = this;
6491 var selfIsArrayLike = self.length !== undefined;
6492 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6493
6494 var cy = this._private.cy || this;
6495
6496 if (!cy.styleEnabled()) {
6497 return this;
6498 }
6499
6500 for (var i = 0; i < all.length; i++) {
6501 var ele = all[i];
6502 var _p = ele._private;
6503 var anis = _p.animation.current;
6504
6505 for (var j = 0; j < anis.length; j++) {
6506 var ani = anis[j];
6507 var ani_p = ani._private;
6508
6509 if (jumpToEnd) {
6510 // next iteration of the animation loop, the animation
6511 // will go straight to the end and be removed
6512 ani_p.duration = 0;
6513 }
6514 } // clear the queue of future animations
6515
6516
6517 if (clearQueue) {
6518 _p.animation.queue = [];
6519 }
6520
6521 if (!jumpToEnd) {
6522 _p.animation.current = [];
6523 }
6524 } // we have to notify (the animation loop doesn't do it for us on `stop`)
6525
6526
6527 cy.notify('draw');
6528 return this;
6529 };
6530 } // stop
6531
6532}; // define
6533
6534var define$1 = {
6535 // access data field
6536 data: function data(params) {
6537 var defaults = {
6538 field: 'data',
6539 bindingEvent: 'data',
6540 allowBinding: false,
6541 allowSetting: false,
6542 allowGetting: false,
6543 settingEvent: 'data',
6544 settingTriggersEvent: false,
6545 triggerFnName: 'trigger',
6546 immutableKeys: {},
6547 // key => true if immutable
6548 updateStyle: false,
6549 beforeGet: function beforeGet(self) {},
6550 beforeSet: function beforeSet(self, obj) {},
6551 onSet: function onSet(self) {},
6552 canSet: function canSet(self) {
6553 return true;
6554 }
6555 };
6556 params = extend({}, defaults, params);
6557 return function dataImpl(name, value) {
6558 var p = params;
6559 var self = this;
6560 var selfIsArrayLike = self.length !== undefined;
6561 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6562
6563 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
6564
6565 if (string(name)) {
6566 // set or get property
6567 // .data('foo')
6568 if (p.allowGetting && value === undefined) {
6569 // get
6570 var ret;
6571
6572 if (single) {
6573 p.beforeGet(single);
6574 ret = single._private[p.field][name];
6575 }
6576
6577 return ret; // .data('foo', 'bar')
6578 } else if (p.allowSetting && value !== undefined) {
6579 // set
6580 var valid = !p.immutableKeys[name];
6581
6582 if (valid) {
6583 var change = _defineProperty({}, name, value);
6584
6585 p.beforeSet(self, change);
6586
6587 for (var i = 0, l = all.length; i < l; i++) {
6588 var ele = all[i];
6589
6590 if (p.canSet(ele)) {
6591 ele._private[p.field][name] = value;
6592 }
6593 } // update mappers if asked
6594
6595
6596 if (p.updateStyle) {
6597 self.updateStyle();
6598 } // call onSet callback
6599
6600
6601 p.onSet(self);
6602
6603 if (p.settingTriggersEvent) {
6604 self[p.triggerFnName](p.settingEvent);
6605 }
6606 }
6607 } // .data({ 'foo': 'bar' })
6608
6609 } else if (p.allowSetting && plainObject(name)) {
6610 // extend
6611 var obj = name;
6612 var k, v;
6613 var keys = Object.keys(obj);
6614 p.beforeSet(self, obj);
6615
6616 for (var _i = 0; _i < keys.length; _i++) {
6617 k = keys[_i];
6618 v = obj[k];
6619
6620 var _valid = !p.immutableKeys[k];
6621
6622 if (_valid) {
6623 for (var j = 0; j < all.length; j++) {
6624 var _ele = all[j];
6625
6626 if (p.canSet(_ele)) {
6627 _ele._private[p.field][k] = v;
6628 }
6629 }
6630 }
6631 } // update mappers if asked
6632
6633
6634 if (p.updateStyle) {
6635 self.updateStyle();
6636 } // call onSet callback
6637
6638
6639 p.onSet(self);
6640
6641 if (p.settingTriggersEvent) {
6642 self[p.triggerFnName](p.settingEvent);
6643 } // .data(function(){ ... })
6644
6645 } else if (p.allowBinding && fn(name)) {
6646 // bind to event
6647 var fn$1 = name;
6648 self.on(p.bindingEvent, fn$1); // .data()
6649 } else if (p.allowGetting && name === undefined) {
6650 // get whole object
6651 var _ret;
6652
6653 if (single) {
6654 p.beforeGet(single);
6655 _ret = single._private[p.field];
6656 }
6657
6658 return _ret;
6659 }
6660
6661 return self; // maintain chainability
6662 }; // function
6663 },
6664 // data
6665 // remove data field
6666 removeData: function removeData(params) {
6667 var defaults = {
6668 field: 'data',
6669 event: 'data',
6670 triggerFnName: 'trigger',
6671 triggerEvent: false,
6672 immutableKeys: {} // key => true if immutable
6673
6674 };
6675 params = extend({}, defaults, params);
6676 return function removeDataImpl(names) {
6677 var p = params;
6678 var self = this;
6679 var selfIsArrayLike = self.length !== undefined;
6680 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6681 // .removeData('foo bar')
6682
6683 if (string(names)) {
6684 // then get the list of keys, and delete them
6685 var keys = names.split(/\s+/);
6686 var l = keys.length;
6687
6688 for (var i = 0; i < l; i++) {
6689 // delete each non-empty key
6690 var key = keys[i];
6691
6692 if (emptyString(key)) {
6693 continue;
6694 }
6695
6696 var valid = !p.immutableKeys[key]; // not valid if immutable
6697
6698 if (valid) {
6699 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
6700 all[i_a]._private[p.field][key] = undefined;
6701 }
6702 }
6703 }
6704
6705 if (p.triggerEvent) {
6706 self[p.triggerFnName](p.event);
6707 } // .removeData()
6708
6709 } else if (names === undefined) {
6710 // then delete all keys
6711 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
6712 var _privateFields = all[_i_a]._private[p.field];
6713
6714 var _keys = Object.keys(_privateFields);
6715
6716 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
6717 var _key = _keys[_i2];
6718 var validKeyToDelete = !p.immutableKeys[_key];
6719
6720 if (validKeyToDelete) {
6721 _privateFields[_key] = undefined;
6722 }
6723 }
6724 }
6725
6726 if (p.triggerEvent) {
6727 self[p.triggerFnName](p.event);
6728 }
6729 }
6730
6731 return self; // maintain chaining
6732 }; // function
6733 } // removeData
6734
6735}; // define
6736
6737var define$2 = {
6738 eventAliasesOn: function eventAliasesOn(proto) {
6739 var p = proto;
6740 p.addListener = p.listen = p.bind = p.on;
6741 p.unlisten = p.unbind = p.off = p.removeListener;
6742 p.trigger = p.emit; // this is just a wrapper alias of .on()
6743
6744 p.pon = p.promiseOn = function (events, selector) {
6745 var self = this;
6746 var args = Array.prototype.slice.call(arguments, 0);
6747 return new Promise$1(function (resolve, reject) {
6748 var callback = function callback(e) {
6749 self.off.apply(self, offArgs);
6750 resolve(e);
6751 };
6752
6753 var onArgs = args.concat([callback]);
6754 var offArgs = onArgs.concat([]);
6755 self.on.apply(self, onArgs);
6756 });
6757 };
6758 }
6759}; // define
6760
6761// use this module to cherry pick functions into your prototype
6762var define$3 = {};
6763[define, define$1, define$2].forEach(function (m) {
6764 extend(define$3, m);
6765});
6766
6767var elesfn$d = {
6768 animate: define$3.animate(),
6769 animation: define$3.animation(),
6770 animated: define$3.animated(),
6771 clearQueue: define$3.clearQueue(),
6772 delay: define$3.delay(),
6773 delayAnimation: define$3.delayAnimation(),
6774 stop: define$3.stop()
6775};
6776
6777var elesfn$e = {
6778 classes: function classes(_classes) {
6779 var self = this;
6780
6781 if (_classes === undefined) {
6782 var ret = [];
6783
6784 self[0]._private.classes.forEach(function (cls) {
6785 return ret.push(cls);
6786 });
6787
6788 return ret;
6789 } else if (!array(_classes)) {
6790 // extract classes from string
6791 _classes = (_classes || '').match(/\S+/g) || [];
6792 }
6793
6794 var changed = [];
6795 var classesSet = new Set$1(_classes); // check and update each ele
6796
6797 for (var j = 0; j < self.length; j++) {
6798 var ele = self[j];
6799 var _p = ele._private;
6800 var eleClasses = _p.classes;
6801 var changedEle = false; // check if ele has all of the passed classes
6802
6803 for (var i = 0; i < _classes.length; i++) {
6804 var cls = _classes[i];
6805 var eleHasClass = eleClasses.has(cls);
6806
6807 if (!eleHasClass) {
6808 changedEle = true;
6809 break;
6810 }
6811 } // check if ele has classes outside of those passed
6812
6813
6814 if (!changedEle) {
6815 changedEle = eleClasses.size !== _classes.length;
6816 }
6817
6818 if (changedEle) {
6819 _p.classes = classesSet;
6820 changed.push(ele);
6821 }
6822 } // trigger update style on those eles that had class changes
6823
6824
6825 if (changed.length > 0) {
6826 this.spawn(changed).updateStyle().emit('class');
6827 }
6828
6829 return self;
6830 },
6831 addClass: function addClass(classes) {
6832 return this.toggleClass(classes, true);
6833 },
6834 hasClass: function hasClass(className) {
6835 var ele = this[0];
6836 return ele != null && ele._private.classes.has(className);
6837 },
6838 toggleClass: function toggleClass(classes, toggle) {
6839 if (!array(classes)) {
6840 // extract classes from string
6841 classes = classes.match(/\S+/g) || [];
6842 }
6843
6844 var self = this;
6845 var toggleUndefd = toggle === undefined;
6846 var changed = []; // eles who had classes changed
6847
6848 for (var i = 0, il = self.length; i < il; i++) {
6849 var ele = self[i];
6850 var eleClasses = ele._private.classes;
6851 var changedEle = false;
6852
6853 for (var j = 0; j < classes.length; j++) {
6854 var cls = classes[j];
6855 var hasClass = eleClasses.has(cls);
6856 var changedNow = false;
6857
6858 if (toggle || toggleUndefd && !hasClass) {
6859 eleClasses.add(cls);
6860 changedNow = true;
6861 } else if (!toggle || toggleUndefd && hasClass) {
6862 eleClasses["delete"](cls);
6863 changedNow = true;
6864 }
6865
6866 if (!changedEle && changedNow) {
6867 changed.push(ele);
6868 changedEle = true;
6869 }
6870 } // for j classes
6871
6872 } // for i eles
6873 // trigger update style on those eles that had class changes
6874
6875
6876 if (changed.length > 0) {
6877 this.spawn(changed).updateStyle().emit('class');
6878 }
6879
6880 return self;
6881 },
6882 removeClass: function removeClass(classes) {
6883 return this.toggleClass(classes, false);
6884 },
6885 flashClass: function flashClass(classes, duration) {
6886 var self = this;
6887
6888 if (duration == null) {
6889 duration = 250;
6890 } else if (duration === 0) {
6891 return self; // nothing to do really
6892 }
6893
6894 self.addClass(classes);
6895 setTimeout(function () {
6896 self.removeClass(classes);
6897 }, duration);
6898 return self;
6899 }
6900};
6901elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
6902
6903var tokens = {
6904 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
6905 // chars we need to escape in let names, etc
6906 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
6907 // binary comparison op (used in data selectors)
6908 boolOp: '\\?|\\!|\\^',
6909 // boolean (unary) operators (used in data selectors)
6910 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
6911 // string literals (used in data selectors) -- doublequotes | singlequotes
6912 number: number$1,
6913 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
6914 meta: 'degree|indegree|outdegree',
6915 // allowed metadata fields (i.e. allowed functions to use from Collection)
6916 separator: '\\s*,\\s*',
6917 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
6918 descendant: '\\s+',
6919 child: '\\s+>\\s+',
6920 subject: '\\$',
6921 group: 'node|edge|\\*',
6922 directedEdge: '\\s+->\\s+',
6923 undirectedEdge: '\\s+<->\\s+'
6924};
6925tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
6926
6927tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
6928
6929tokens.className = tokens.variable; // a class name (follows variable conventions)
6930
6931tokens.id = tokens.variable; // an element id (follows variable conventions)
6932
6933(function () {
6934 var ops, op, i; // add @ variants to comparatorOp
6935
6936 ops = tokens.comparatorOp.split('|');
6937
6938 for (i = 0; i < ops.length; i++) {
6939 op = ops[i];
6940 tokens.comparatorOp += '|@' + op;
6941 } // add ! variants to comparatorOp
6942
6943
6944 ops = tokens.comparatorOp.split('|');
6945
6946 for (i = 0; i < ops.length; i++) {
6947 op = ops[i];
6948
6949 if (op.indexOf('!') >= 0) {
6950 continue;
6951 } // skip ops that explicitly contain !
6952
6953
6954 if (op === '=') {
6955 continue;
6956 } // skip = b/c != is explicitly defined
6957
6958
6959 tokens.comparatorOp += '|\\!' + op;
6960 }
6961})();
6962
6963/**
6964 * Make a new query object
6965 *
6966 * @prop type {Type} The type enum (int) of the query
6967 * @prop checks List of checks to make against an ele to test for a match
6968 */
6969var newQuery = function newQuery() {
6970 return {
6971 checks: []
6972 };
6973};
6974
6975/**
6976 * A check type enum-like object. Uses integer values for fast match() lookup.
6977 * The ordering does not matter as long as the ints are unique.
6978 */
6979var Type = {
6980 /** E.g. node */
6981 GROUP: 0,
6982
6983 /** A collection of elements */
6984 COLLECTION: 1,
6985
6986 /** A filter(ele) function */
6987 FILTER: 2,
6988
6989 /** E.g. [foo > 1] */
6990 DATA_COMPARE: 3,
6991
6992 /** E.g. [foo] */
6993 DATA_EXIST: 4,
6994
6995 /** E.g. [?foo] */
6996 DATA_BOOL: 5,
6997
6998 /** E.g. [[degree > 2]] */
6999 META_COMPARE: 6,
7000
7001 /** E.g. :selected */
7002 STATE: 7,
7003
7004 /** E.g. #foo */
7005 ID: 8,
7006
7007 /** E.g. .foo */
7008 CLASS: 9,
7009
7010 /** E.g. #foo <-> #bar */
7011 UNDIRECTED_EDGE: 10,
7012
7013 /** E.g. #foo -> #bar */
7014 DIRECTED_EDGE: 11,
7015
7016 /** E.g. $#foo -> #bar */
7017 NODE_SOURCE: 12,
7018
7019 /** E.g. #foo -> $#bar */
7020 NODE_TARGET: 13,
7021
7022 /** E.g. $#foo <-> #bar */
7023 NODE_NEIGHBOR: 14,
7024
7025 /** E.g. #foo > #bar */
7026 CHILD: 15,
7027
7028 /** E.g. #foo #bar */
7029 DESCENDANT: 16,
7030
7031 /** E.g. $#foo > #bar */
7032 PARENT: 17,
7033
7034 /** E.g. $#foo #bar */
7035 ANCESTOR: 18,
7036
7037 /** E.g. #foo > $bar > #baz */
7038 COMPOUND_SPLIT: 19,
7039
7040 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7041 TRUE: 20
7042};
7043
7044var stateSelectors = [{
7045 selector: ':selected',
7046 matches: function matches(ele) {
7047 return ele.selected();
7048 }
7049}, {
7050 selector: ':unselected',
7051 matches: function matches(ele) {
7052 return !ele.selected();
7053 }
7054}, {
7055 selector: ':selectable',
7056 matches: function matches(ele) {
7057 return ele.selectable();
7058 }
7059}, {
7060 selector: ':unselectable',
7061 matches: function matches(ele) {
7062 return !ele.selectable();
7063 }
7064}, {
7065 selector: ':locked',
7066 matches: function matches(ele) {
7067 return ele.locked();
7068 }
7069}, {
7070 selector: ':unlocked',
7071 matches: function matches(ele) {
7072 return !ele.locked();
7073 }
7074}, {
7075 selector: ':visible',
7076 matches: function matches(ele) {
7077 return ele.visible();
7078 }
7079}, {
7080 selector: ':hidden',
7081 matches: function matches(ele) {
7082 return !ele.visible();
7083 }
7084}, {
7085 selector: ':transparent',
7086 matches: function matches(ele) {
7087 return ele.transparent();
7088 }
7089}, {
7090 selector: ':grabbed',
7091 matches: function matches(ele) {
7092 return ele.grabbed();
7093 }
7094}, {
7095 selector: ':free',
7096 matches: function matches(ele) {
7097 return !ele.grabbed();
7098 }
7099}, {
7100 selector: ':removed',
7101 matches: function matches(ele) {
7102 return ele.removed();
7103 }
7104}, {
7105 selector: ':inside',
7106 matches: function matches(ele) {
7107 return !ele.removed();
7108 }
7109}, {
7110 selector: ':grabbable',
7111 matches: function matches(ele) {
7112 return ele.grabbable();
7113 }
7114}, {
7115 selector: ':ungrabbable',
7116 matches: function matches(ele) {
7117 return !ele.grabbable();
7118 }
7119}, {
7120 selector: ':animated',
7121 matches: function matches(ele) {
7122 return ele.animated();
7123 }
7124}, {
7125 selector: ':unanimated',
7126 matches: function matches(ele) {
7127 return !ele.animated();
7128 }
7129}, {
7130 selector: ':parent',
7131 matches: function matches(ele) {
7132 return ele.isParent();
7133 }
7134}, {
7135 selector: ':childless',
7136 matches: function matches(ele) {
7137 return ele.isChildless();
7138 }
7139}, {
7140 selector: ':child',
7141 matches: function matches(ele) {
7142 return ele.isChild();
7143 }
7144}, {
7145 selector: ':orphan',
7146 matches: function matches(ele) {
7147 return ele.isOrphan();
7148 }
7149}, {
7150 selector: ':nonorphan',
7151 matches: function matches(ele) {
7152 return ele.isChild();
7153 }
7154}, {
7155 selector: ':compound',
7156 matches: function matches(ele) {
7157 if (ele.isNode()) {
7158 return ele.isParent();
7159 } else {
7160 return ele.source().isParent() || ele.target().isParent();
7161 }
7162 }
7163}, {
7164 selector: ':loop',
7165 matches: function matches(ele) {
7166 return ele.isLoop();
7167 }
7168}, {
7169 selector: ':simple',
7170 matches: function matches(ele) {
7171 return ele.isSimple();
7172 }
7173}, {
7174 selector: ':active',
7175 matches: function matches(ele) {
7176 return ele.active();
7177 }
7178}, {
7179 selector: ':inactive',
7180 matches: function matches(ele) {
7181 return !ele.active();
7182 }
7183}, {
7184 selector: ':backgrounding',
7185 matches: function matches(ele) {
7186 return ele.backgrounding();
7187 }
7188}, {
7189 selector: ':nonbackgrounding',
7190 matches: function matches(ele) {
7191 return !ele.backgrounding();
7192 }
7193}].sort(function (a, b) {
7194 // n.b. selectors that are starting substrings of others must have the longer ones first
7195 return descending(a.selector, b.selector);
7196});
7197
7198var lookup = function () {
7199 var selToFn = {};
7200 var s;
7201
7202 for (var i = 0; i < stateSelectors.length; i++) {
7203 s = stateSelectors[i];
7204 selToFn[s.selector] = s.matches;
7205 }
7206
7207 return selToFn;
7208}();
7209
7210var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7211 return lookup[sel](ele);
7212};
7213var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7214 return s.selector;
7215}).join('|') + ')';
7216
7217// so that values get compared properly in Selector.filter()
7218
7219var cleanMetaChars = function cleanMetaChars(str) {
7220 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7221 return $1;
7222 });
7223};
7224
7225var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7226 selector[selector.length - 1] = replacementQuery;
7227}; // NOTE: add new expression syntax here to have it recognised by the parser;
7228// - a query contains all adjacent (i.e. no separator in between) expressions;
7229// - the current query is stored in selector[i]
7230// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7231
7232
7233var exprs = [{
7234 name: 'group',
7235 // just used for identifying when debugging
7236 query: true,
7237 regex: '(' + tokens.group + ')',
7238 populate: function populate(selector, query, _ref) {
7239 var _ref2 = _slicedToArray(_ref, 1),
7240 group = _ref2[0];
7241
7242 query.checks.push({
7243 type: Type.GROUP,
7244 value: group === '*' ? group : group + 's'
7245 });
7246 }
7247}, {
7248 name: 'state',
7249 query: true,
7250 regex: stateSelectorRegex,
7251 populate: function populate(selector, query, _ref3) {
7252 var _ref4 = _slicedToArray(_ref3, 1),
7253 state = _ref4[0];
7254
7255 query.checks.push({
7256 type: Type.STATE,
7257 value: state
7258 });
7259 }
7260}, {
7261 name: 'id',
7262 query: true,
7263 regex: '\\#(' + tokens.id + ')',
7264 populate: function populate(selector, query, _ref5) {
7265 var _ref6 = _slicedToArray(_ref5, 1),
7266 id = _ref6[0];
7267
7268 query.checks.push({
7269 type: Type.ID,
7270 value: cleanMetaChars(id)
7271 });
7272 }
7273}, {
7274 name: 'className',
7275 query: true,
7276 regex: '\\.(' + tokens.className + ')',
7277 populate: function populate(selector, query, _ref7) {
7278 var _ref8 = _slicedToArray(_ref7, 1),
7279 className = _ref8[0];
7280
7281 query.checks.push({
7282 type: Type.CLASS,
7283 value: cleanMetaChars(className)
7284 });
7285 }
7286}, {
7287 name: 'dataExists',
7288 query: true,
7289 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
7290 populate: function populate(selector, query, _ref9) {
7291 var _ref10 = _slicedToArray(_ref9, 1),
7292 variable = _ref10[0];
7293
7294 query.checks.push({
7295 type: Type.DATA_EXIST,
7296 field: cleanMetaChars(variable)
7297 });
7298 }
7299}, {
7300 name: 'dataCompare',
7301 query: true,
7302 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
7303 populate: function populate(selector, query, _ref11) {
7304 var _ref12 = _slicedToArray(_ref11, 3),
7305 variable = _ref12[0],
7306 comparatorOp = _ref12[1],
7307 value = _ref12[2];
7308
7309 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
7310
7311 if (valueIsString) {
7312 value = value.substring(1, value.length - 1);
7313 } else {
7314 value = parseFloat(value);
7315 }
7316
7317 query.checks.push({
7318 type: Type.DATA_COMPARE,
7319 field: cleanMetaChars(variable),
7320 operator: comparatorOp,
7321 value: value
7322 });
7323 }
7324}, {
7325 name: 'dataBool',
7326 query: true,
7327 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
7328 populate: function populate(selector, query, _ref13) {
7329 var _ref14 = _slicedToArray(_ref13, 2),
7330 boolOp = _ref14[0],
7331 variable = _ref14[1];
7332
7333 query.checks.push({
7334 type: Type.DATA_BOOL,
7335 field: cleanMetaChars(variable),
7336 operator: boolOp
7337 });
7338 }
7339}, {
7340 name: 'metaCompare',
7341 query: true,
7342 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
7343 populate: function populate(selector, query, _ref15) {
7344 var _ref16 = _slicedToArray(_ref15, 3),
7345 meta = _ref16[0],
7346 comparatorOp = _ref16[1],
7347 number = _ref16[2];
7348
7349 query.checks.push({
7350 type: Type.META_COMPARE,
7351 field: cleanMetaChars(meta),
7352 operator: comparatorOp,
7353 value: parseFloat(number)
7354 });
7355 }
7356}, {
7357 name: 'nextQuery',
7358 separator: true,
7359 regex: tokens.separator,
7360 populate: function populate(selector, query) {
7361 var currentSubject = selector.currentSubject;
7362 var edgeCount = selector.edgeCount;
7363 var compoundCount = selector.compoundCount;
7364 var lastQ = selector[selector.length - 1];
7365
7366 if (currentSubject != null) {
7367 lastQ.subject = currentSubject;
7368 selector.currentSubject = null;
7369 }
7370
7371 lastQ.edgeCount = edgeCount;
7372 lastQ.compoundCount = compoundCount;
7373 selector.edgeCount = 0;
7374 selector.compoundCount = 0; // go on to next query
7375
7376 var nextQuery = selector[selector.length++] = newQuery();
7377 return nextQuery; // this is the new query to be filled by the following exprs
7378 }
7379}, {
7380 name: 'directedEdge',
7381 separator: true,
7382 regex: tokens.directedEdge,
7383 populate: function populate(selector, query) {
7384 if (selector.currentSubject == null) {
7385 // undirected edge
7386 var edgeQuery = newQuery();
7387 var source = query;
7388 var target = newQuery();
7389 edgeQuery.checks.push({
7390 type: Type.DIRECTED_EDGE,
7391 source: source,
7392 target: target
7393 }); // the query in the selector should be the edge rather than the source
7394
7395 replaceLastQuery(selector, query, edgeQuery);
7396 selector.edgeCount++; // we're now populating the target query with expressions that follow
7397
7398 return target;
7399 } else {
7400 // source/target
7401 var srcTgtQ = newQuery();
7402 var _source = query;
7403
7404 var _target = newQuery();
7405
7406 srcTgtQ.checks.push({
7407 type: Type.NODE_SOURCE,
7408 source: _source,
7409 target: _target
7410 }); // the query in the selector should be the neighbourhood rather than the node
7411
7412 replaceLastQuery(selector, query, srcTgtQ);
7413 selector.edgeCount++;
7414 return _target; // now populating the target with the following expressions
7415 }
7416 }
7417}, {
7418 name: 'undirectedEdge',
7419 separator: true,
7420 regex: tokens.undirectedEdge,
7421 populate: function populate(selector, query) {
7422 if (selector.currentSubject == null) {
7423 // undirected edge
7424 var edgeQuery = newQuery();
7425 var source = query;
7426 var target = newQuery();
7427 edgeQuery.checks.push({
7428 type: Type.UNDIRECTED_EDGE,
7429 nodes: [source, target]
7430 }); // the query in the selector should be the edge rather than the source
7431
7432 replaceLastQuery(selector, query, edgeQuery);
7433 selector.edgeCount++; // we're now populating the target query with expressions that follow
7434
7435 return target;
7436 } else {
7437 // neighbourhood
7438 var nhoodQ = newQuery();
7439 var node = query;
7440 var neighbor = newQuery();
7441 nhoodQ.checks.push({
7442 type: Type.NODE_NEIGHBOR,
7443 node: node,
7444 neighbor: neighbor
7445 }); // the query in the selector should be the neighbourhood rather than the node
7446
7447 replaceLastQuery(selector, query, nhoodQ);
7448 return neighbor; // now populating the neighbor with following expressions
7449 }
7450 }
7451}, {
7452 name: 'child',
7453 separator: true,
7454 regex: tokens.child,
7455 populate: function populate(selector, query) {
7456 if (selector.currentSubject == null) {
7457 // default: child query
7458 var parentChildQuery = newQuery();
7459 var child = newQuery();
7460 var parent = selector[selector.length - 1];
7461 parentChildQuery.checks.push({
7462 type: Type.CHILD,
7463 parent: parent,
7464 child: child
7465 }); // the query in the selector should be the '>' itself
7466
7467 replaceLastQuery(selector, query, parentChildQuery);
7468 selector.compoundCount++; // we're now populating the child query with expressions that follow
7469
7470 return child;
7471 } else if (selector.currentSubject === query) {
7472 // compound split query
7473 var compound = newQuery();
7474 var left = selector[selector.length - 1];
7475 var right = newQuery();
7476 var subject = newQuery();
7477
7478 var _child = newQuery();
7479
7480 var _parent = newQuery(); // set up the root compound q
7481
7482
7483 compound.checks.push({
7484 type: Type.COMPOUND_SPLIT,
7485 left: left,
7486 right: right,
7487 subject: subject
7488 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7489
7490 subject.checks = query.checks; // take the checks from the left
7491
7492 query.checks = [{
7493 type: Type.TRUE
7494 }]; // checks under left refs the subject implicitly
7495 // set up the right q
7496
7497 _parent.checks.push({
7498 type: Type.TRUE
7499 }); // parent implicitly refs the subject
7500
7501
7502 right.checks.push({
7503 type: Type.PARENT,
7504 // type is swapped on right side queries
7505 parent: _parent,
7506 child: _child // empty for now
7507
7508 });
7509 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7510
7511 selector.currentSubject = subject;
7512 selector.compoundCount++;
7513 return _child; // now populating the right side's child
7514 } else {
7515 // parent query
7516 // info for parent query
7517 var _parent2 = newQuery();
7518
7519 var _child2 = newQuery();
7520
7521 var pcQChecks = [{
7522 type: Type.PARENT,
7523 parent: _parent2,
7524 child: _child2
7525 }]; // the parent-child query takes the place of the query previously being populated
7526
7527 _parent2.checks = query.checks; // the previous query contains the checks for the parent
7528
7529 query.checks = pcQChecks; // pc query takes over
7530
7531 selector.compoundCount++;
7532 return _child2; // we're now populating the child
7533 }
7534 }
7535}, {
7536 name: 'descendant',
7537 separator: true,
7538 regex: tokens.descendant,
7539 populate: function populate(selector, query) {
7540 if (selector.currentSubject == null) {
7541 // default: descendant query
7542 var ancChQuery = newQuery();
7543 var descendant = newQuery();
7544 var ancestor = selector[selector.length - 1];
7545 ancChQuery.checks.push({
7546 type: Type.DESCENDANT,
7547 ancestor: ancestor,
7548 descendant: descendant
7549 }); // the query in the selector should be the '>' itself
7550
7551 replaceLastQuery(selector, query, ancChQuery);
7552 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
7553
7554 return descendant;
7555 } else if (selector.currentSubject === query) {
7556 // compound split query
7557 var compound = newQuery();
7558 var left = selector[selector.length - 1];
7559 var right = newQuery();
7560 var subject = newQuery();
7561
7562 var _descendant = newQuery();
7563
7564 var _ancestor = newQuery(); // set up the root compound q
7565
7566
7567 compound.checks.push({
7568 type: Type.COMPOUND_SPLIT,
7569 left: left,
7570 right: right,
7571 subject: subject
7572 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7573
7574 subject.checks = query.checks; // take the checks from the left
7575
7576 query.checks = [{
7577 type: Type.TRUE
7578 }]; // checks under left refs the subject implicitly
7579 // set up the right q
7580
7581 _ancestor.checks.push({
7582 type: Type.TRUE
7583 }); // ancestor implicitly refs the subject
7584
7585
7586 right.checks.push({
7587 type: Type.ANCESTOR,
7588 // type is swapped on right side queries
7589 ancestor: _ancestor,
7590 descendant: _descendant // empty for now
7591
7592 });
7593 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7594
7595 selector.currentSubject = subject;
7596 selector.compoundCount++;
7597 return _descendant; // now populating the right side's descendant
7598 } else {
7599 // ancestor query
7600 // info for parent query
7601 var _ancestor2 = newQuery();
7602
7603 var _descendant2 = newQuery();
7604
7605 var adQChecks = [{
7606 type: Type.ANCESTOR,
7607 ancestor: _ancestor2,
7608 descendant: _descendant2
7609 }]; // the parent-child query takes the place of the query previously being populated
7610
7611 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
7612
7613 query.checks = adQChecks; // pc query takes over
7614
7615 selector.compoundCount++;
7616 return _descendant2; // we're now populating the child
7617 }
7618 }
7619}, {
7620 name: 'subject',
7621 modifier: true,
7622 regex: tokens.subject,
7623 populate: function populate(selector, query) {
7624 if (selector.currentSubject != null && selector.currentSubject !== query) {
7625 warn('Redefinition of subject in selector `' + selector.toString() + '`');
7626 return false;
7627 }
7628
7629 selector.currentSubject = query;
7630 var topQ = selector[selector.length - 1];
7631 var topChk = topQ.checks[0];
7632 var topType = topChk == null ? null : topChk.type;
7633
7634 if (topType === Type.DIRECTED_EDGE) {
7635 // directed edge with subject on the target
7636 // change to target node check
7637 topChk.type = Type.NODE_TARGET;
7638 } else if (topType === Type.UNDIRECTED_EDGE) {
7639 // undirected edge with subject on the second node
7640 // change to neighbor check
7641 topChk.type = Type.NODE_NEIGHBOR;
7642 topChk.node = topChk.nodes[1]; // second node is subject
7643
7644 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
7645
7646 topChk.nodes = null;
7647 }
7648 }
7649}];
7650exprs.forEach(function (e) {
7651 return e.regexObj = new RegExp('^' + e.regex);
7652});
7653
7654/**
7655 * Of all the expressions, find the first match in the remaining text.
7656 * @param {string} remaining The remaining text to parse
7657 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
7658 */
7659
7660var consumeExpr = function consumeExpr(remaining) {
7661 var expr;
7662 var match;
7663 var name;
7664
7665 for (var j = 0; j < exprs.length; j++) {
7666 var e = exprs[j];
7667 var n = e.name;
7668 var m = remaining.match(e.regexObj);
7669
7670 if (m != null) {
7671 match = m;
7672 expr = e;
7673 name = n;
7674 var consumed = m[0];
7675 remaining = remaining.substring(consumed.length);
7676 break; // we've consumed one expr, so we can return now
7677 }
7678 }
7679
7680 return {
7681 expr: expr,
7682 match: match,
7683 name: name,
7684 remaining: remaining
7685 };
7686};
7687/**
7688 * Consume all the leading whitespace
7689 * @param {string} remaining The text to consume
7690 * @returns The text with the leading whitespace removed
7691 */
7692
7693
7694var consumeWhitespace = function consumeWhitespace(remaining) {
7695 var match = remaining.match(/^\s+/);
7696
7697 if (match) {
7698 var consumed = match[0];
7699 remaining = remaining.substring(consumed.length);
7700 }
7701
7702 return remaining;
7703};
7704/**
7705 * Parse the string and store the parsed representation in the Selector.
7706 * @param {string} selector The selector string
7707 * @returns `true` if the selector was successfully parsed, `false` otherwise
7708 */
7709
7710
7711var parse = function parse(selector) {
7712 var self = this;
7713 var remaining = self.inputText = selector;
7714 var currentQuery = self[0] = newQuery();
7715 self.length = 1;
7716 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
7717
7718 for (;;) {
7719 var exprInfo = consumeExpr(remaining);
7720
7721 if (exprInfo.expr == null) {
7722 warn('The selector `' + selector + '`is invalid');
7723 return false;
7724 } else {
7725 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
7726
7727 var ret = exprInfo.expr.populate(self, currentQuery, args);
7728
7729 if (ret === false) {
7730 return false; // exit if population failed
7731 } else if (ret != null) {
7732 currentQuery = ret; // change the current query to be filled if the expr specifies
7733 }
7734 }
7735
7736 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
7737
7738 if (remaining.match(/^\s*$/)) {
7739 break;
7740 }
7741 }
7742
7743 var lastQ = self[self.length - 1];
7744
7745 if (self.currentSubject != null) {
7746 lastQ.subject = self.currentSubject;
7747 }
7748
7749 lastQ.edgeCount = self.edgeCount;
7750 lastQ.compoundCount = self.compoundCount;
7751
7752 for (var i = 0; i < self.length; i++) {
7753 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
7754
7755 if (q.compoundCount > 0 && q.edgeCount > 0) {
7756 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
7757 return false;
7758 }
7759
7760 if (q.edgeCount > 1) {
7761 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
7762 return false;
7763 } else if (q.edgeCount === 1) {
7764 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.');
7765 }
7766 }
7767
7768 return true; // success
7769};
7770/**
7771 * Get the selector represented as a string. This value uses default formatting,
7772 * so things like spacing may differ from the input text passed to the constructor.
7773 * @returns {string} The selector string
7774 */
7775
7776
7777var toString = function toString() {
7778 if (this.toStringCache != null) {
7779 return this.toStringCache;
7780 }
7781
7782 var clean = function clean(obj) {
7783 if (obj == null) {
7784 return '';
7785 } else {
7786 return obj;
7787 }
7788 };
7789
7790 var cleanVal = function cleanVal(val) {
7791 if (string(val)) {
7792 return '"' + val + '"';
7793 } else {
7794 return clean(val);
7795 }
7796 };
7797
7798 var space = function space(val) {
7799 return ' ' + val + ' ';
7800 };
7801
7802 var checkToString = function checkToString(check, subject) {
7803 var type = check.type,
7804 value = check.value;
7805
7806 switch (type) {
7807 case Type.GROUP:
7808 {
7809 var group = clean(value);
7810 return group.substring(0, group.length - 1);
7811 }
7812
7813 case Type.DATA_COMPARE:
7814 {
7815 var field = check.field,
7816 operator = check.operator;
7817 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
7818 }
7819
7820 case Type.DATA_BOOL:
7821 {
7822 var _operator = check.operator,
7823 _field = check.field;
7824 return '[' + clean(_operator) + _field + ']';
7825 }
7826
7827 case Type.DATA_EXIST:
7828 {
7829 var _field2 = check.field;
7830 return '[' + _field2 + ']';
7831 }
7832
7833 case Type.META_COMPARE:
7834 {
7835 var _operator2 = check.operator,
7836 _field3 = check.field;
7837 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
7838 }
7839
7840 case Type.STATE:
7841 {
7842 return value;
7843 }
7844
7845 case Type.ID:
7846 {
7847 return '#' + value;
7848 }
7849
7850 case Type.CLASS:
7851 {
7852 return '.' + value;
7853 }
7854
7855 case Type.PARENT:
7856 case Type.CHILD:
7857 {
7858 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
7859 }
7860
7861 case Type.ANCESTOR:
7862 case Type.DESCENDANT:
7863 {
7864 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
7865 }
7866
7867 case Type.COMPOUND_SPLIT:
7868 {
7869 var lhs = queryToString(check.left, subject);
7870 var sub = queryToString(check.subject, subject);
7871 var rhs = queryToString(check.right, subject);
7872 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
7873 }
7874
7875 case Type.TRUE:
7876 {
7877 return '';
7878 }
7879 }
7880 };
7881
7882 var queryToString = function queryToString(query, subject) {
7883 return query.checks.reduce(function (str, chk, i) {
7884 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
7885 }, '');
7886 };
7887
7888 var str = '';
7889
7890 for (var i = 0; i < this.length; i++) {
7891 var query = this[i];
7892 str += queryToString(query, query.subject);
7893
7894 if (this.length > 1 && i < this.length - 1) {
7895 str += ', ';
7896 }
7897 }
7898
7899 this.toStringCache = str;
7900 return str;
7901};
7902var parse$1 = {
7903 parse: parse,
7904 toString: toString
7905};
7906
7907var valCmp = function valCmp(fieldVal, operator, value) {
7908 var matches;
7909 var isFieldStr = string(fieldVal);
7910 var isFieldNum = number(fieldVal);
7911 var isValStr = string(value);
7912 var fieldStr, valStr;
7913 var caseInsensitive = false;
7914 var notExpr = false;
7915 var isIneqCmp = false;
7916
7917 if (operator.indexOf('!') >= 0) {
7918 operator = operator.replace('!', '');
7919 notExpr = true;
7920 }
7921
7922 if (operator.indexOf('@') >= 0) {
7923 operator = operator.replace('@', '');
7924 caseInsensitive = true;
7925 }
7926
7927 if (isFieldStr || isValStr || caseInsensitive) {
7928 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
7929 valStr = '' + value;
7930 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
7931 // even if we're comparing numbers
7932
7933
7934 if (caseInsensitive) {
7935 fieldVal = fieldStr = fieldStr.toLowerCase();
7936 value = valStr = valStr.toLowerCase();
7937 }
7938
7939 switch (operator) {
7940 case '*=':
7941 matches = fieldStr.indexOf(valStr) >= 0;
7942 break;
7943
7944 case '$=':
7945 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
7946 break;
7947
7948 case '^=':
7949 matches = fieldStr.indexOf(valStr) === 0;
7950 break;
7951
7952 case '=':
7953 matches = fieldVal === value;
7954 break;
7955
7956 case '>':
7957 isIneqCmp = true;
7958 matches = fieldVal > value;
7959 break;
7960
7961 case '>=':
7962 isIneqCmp = true;
7963 matches = fieldVal >= value;
7964 break;
7965
7966 case '<':
7967 isIneqCmp = true;
7968 matches = fieldVal < value;
7969 break;
7970
7971 case '<=':
7972 isIneqCmp = true;
7973 matches = fieldVal <= value;
7974 break;
7975
7976 default:
7977 matches = false;
7978 break;
7979 } // apply the not op, but null vals for inequalities should always stay non-matching
7980
7981
7982 if (notExpr && (fieldVal != null || !isIneqCmp)) {
7983 matches = !matches;
7984 }
7985
7986 return matches;
7987};
7988var boolCmp = function boolCmp(fieldVal, operator) {
7989 switch (operator) {
7990 case '?':
7991 return fieldVal ? true : false;
7992
7993 case '!':
7994 return fieldVal ? false : true;
7995
7996 case '^':
7997 return fieldVal === undefined;
7998 }
7999};
8000var existCmp = function existCmp(fieldVal) {
8001 return fieldVal !== undefined;
8002};
8003var data = function data(ele, field) {
8004 return ele.data(field);
8005};
8006var meta = function meta(ele, field) {
8007 return ele[field]();
8008};
8009
8010/** A lookup of `match(check, ele)` functions by `Type` int */
8011
8012var match = [];
8013/**
8014 * Returns whether the query matches for the element
8015 * @param query The `{ type, value, ... }` query object
8016 * @param ele The element to compare against
8017*/
8018
8019var matches = function matches(query, ele) {
8020 return query.checks.every(function (chk) {
8021 return match[chk.type](chk, ele);
8022 });
8023};
8024
8025match[Type.GROUP] = function (check, ele) {
8026 var group = check.value;
8027 return group === '*' || group === ele.group();
8028};
8029
8030match[Type.STATE] = function (check, ele) {
8031 var stateSelector = check.value;
8032 return stateSelectorMatches(stateSelector, ele);
8033};
8034
8035match[Type.ID] = function (check, ele) {
8036 var id = check.value;
8037 return ele.id() === id;
8038};
8039
8040match[Type.CLASS] = function (check, ele) {
8041 var cls = check.value;
8042 return ele.hasClass(cls);
8043};
8044
8045match[Type.META_COMPARE] = function (check, ele) {
8046 var field = check.field,
8047 operator = check.operator,
8048 value = check.value;
8049 return valCmp(meta(ele, field), operator, value);
8050};
8051
8052match[Type.DATA_COMPARE] = function (check, ele) {
8053 var field = check.field,
8054 operator = check.operator,
8055 value = check.value;
8056 return valCmp(data(ele, field), operator, value);
8057};
8058
8059match[Type.DATA_BOOL] = function (check, ele) {
8060 var field = check.field,
8061 operator = check.operator;
8062 return boolCmp(data(ele, field), operator);
8063};
8064
8065match[Type.DATA_EXIST] = function (check, ele) {
8066 var field = check.field,
8067 operator = check.operator;
8068 return existCmp(data(ele, field));
8069};
8070
8071match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8072 var qA = check.nodes[0];
8073 var qB = check.nodes[1];
8074 var src = ele.source();
8075 var tgt = ele.target();
8076 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
8077};
8078
8079match[Type.NODE_NEIGHBOR] = function (check, ele) {
8080 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
8081 return n.isNode() && matches(check.neighbor, n);
8082 });
8083};
8084
8085match[Type.DIRECTED_EDGE] = function (check, ele) {
8086 return matches(check.source, ele.source()) && matches(check.target, ele.target());
8087};
8088
8089match[Type.NODE_SOURCE] = function (check, ele) {
8090 return matches(check.source, ele) && ele.outgoers().some(function (n) {
8091 return n.isNode() && matches(check.target, n);
8092 });
8093};
8094
8095match[Type.NODE_TARGET] = function (check, ele) {
8096 return matches(check.target, ele) && ele.incomers().some(function (n) {
8097 return n.isNode() && matches(check.source, n);
8098 });
8099};
8100
8101match[Type.CHILD] = function (check, ele) {
8102 return matches(check.child, ele) && matches(check.parent, ele.parent());
8103};
8104
8105match[Type.PARENT] = function (check, ele) {
8106 return matches(check.parent, ele) && ele.children().some(function (c) {
8107 return matches(check.child, c);
8108 });
8109};
8110
8111match[Type.DESCENDANT] = function (check, ele) {
8112 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
8113 return matches(check.ancestor, a);
8114 });
8115};
8116
8117match[Type.ANCESTOR] = function (check, ele) {
8118 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
8119 return matches(check.descendant, d);
8120 });
8121};
8122
8123match[Type.COMPOUND_SPLIT] = function (check, ele) {
8124 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
8125};
8126
8127match[Type.TRUE] = function () {
8128 return true;
8129};
8130
8131match[Type.COLLECTION] = function (check, ele) {
8132 var collection = check.value;
8133 return collection.has(ele);
8134};
8135
8136match[Type.FILTER] = function (check, ele) {
8137 var filter = check.value;
8138 return filter(ele);
8139};
8140
8141var filter = function filter(collection) {
8142 var self = this; // for 1 id #foo queries, just get the element
8143
8144 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8145 return collection.getElementById(self[0].checks[0].value).collection();
8146 }
8147
8148 var selectorFunction = function selectorFunction(element) {
8149 for (var j = 0; j < self.length; j++) {
8150 var query = self[j];
8151
8152 if (matches(query, element)) {
8153 return true;
8154 }
8155 }
8156
8157 return false;
8158 };
8159
8160 if (self.text() == null) {
8161 selectorFunction = function selectorFunction() {
8162 return true;
8163 };
8164 }
8165
8166 return collection.filter(selectorFunction);
8167}; // filter
8168// does selector match a single element?
8169
8170
8171var matches$1 = function matches$1(ele) {
8172 var self = this;
8173
8174 for (var j = 0; j < self.length; j++) {
8175 var query = self[j];
8176
8177 if (matches(query, ele)) {
8178 return true;
8179 }
8180 }
8181
8182 return false;
8183}; // matches
8184
8185
8186var matching = {
8187 matches: matches$1,
8188 filter: filter
8189};
8190
8191var Selector = function Selector(selector) {
8192 this.inputText = selector;
8193 this.currentSubject = null;
8194 this.compoundCount = 0;
8195 this.edgeCount = 0;
8196 this.length = 0;
8197
8198 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8199 this.addQuery({
8200 checks: [{
8201 type: Type.COLLECTION,
8202 value: selector.collection()
8203 }]
8204 });
8205 } else if (fn(selector)) {
8206 this.addQuery({
8207 checks: [{
8208 type: Type.FILTER,
8209 value: selector
8210 }]
8211 });
8212 } else if (string(selector)) {
8213 if (!this.parse(selector)) {
8214 this.invalid = true;
8215 }
8216 } else {
8217 error('A selector must be created from a string; found ');
8218 }
8219};
8220
8221var selfn = Selector.prototype;
8222[parse$1, matching].forEach(function (p) {
8223 return extend(selfn, p);
8224});
8225
8226selfn.text = function () {
8227 return this.inputText;
8228};
8229
8230selfn.size = function () {
8231 return this.length;
8232};
8233
8234selfn.eq = function (i) {
8235 return this[i];
8236};
8237
8238selfn.sameText = function (otherSel) {
8239 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8240};
8241
8242selfn.addQuery = function (q) {
8243 this[this.length++] = q;
8244};
8245
8246selfn.selector = selfn.toString;
8247
8248var elesfn$f = {
8249 allAre: function allAre(selector) {
8250 var selObj = new Selector(selector);
8251 return this.every(function (ele) {
8252 return selObj.matches(ele);
8253 });
8254 },
8255 is: function is(selector) {
8256 var selObj = new Selector(selector);
8257 return this.some(function (ele) {
8258 return selObj.matches(ele);
8259 });
8260 },
8261 some: function some(fn, thisArg) {
8262 for (var i = 0; i < this.length; i++) {
8263 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8264
8265 if (ret) {
8266 return true;
8267 }
8268 }
8269
8270 return false;
8271 },
8272 every: function every(fn, thisArg) {
8273 for (var i = 0; i < this.length; i++) {
8274 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8275
8276 if (!ret) {
8277 return false;
8278 }
8279 }
8280
8281 return true;
8282 },
8283 same: function same(collection) {
8284 // cheap collection ref check
8285 if (this === collection) {
8286 return true;
8287 }
8288
8289 collection = this.cy().collection(collection);
8290 var thisLength = this.length;
8291 var collectionLength = collection.length; // cheap length check
8292
8293 if (thisLength !== collectionLength) {
8294 return false;
8295 } // cheap element ref check
8296
8297
8298 if (thisLength === 1) {
8299 return this[0] === collection[0];
8300 }
8301
8302 return this.every(function (ele) {
8303 return collection.hasElementWithId(ele.id());
8304 });
8305 },
8306 anySame: function anySame(collection) {
8307 collection = this.cy().collection(collection);
8308 return this.some(function (ele) {
8309 return collection.hasElementWithId(ele.id());
8310 });
8311 },
8312 allAreNeighbors: function allAreNeighbors(collection) {
8313 collection = this.cy().collection(collection);
8314 var nhood = this.neighborhood();
8315 return collection.every(function (ele) {
8316 return nhood.hasElementWithId(ele.id());
8317 });
8318 },
8319 contains: function contains(collection) {
8320 collection = this.cy().collection(collection);
8321 var self = this;
8322 return collection.every(function (ele) {
8323 return self.hasElementWithId(ele.id());
8324 });
8325 }
8326};
8327elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
8328elesfn$f.has = elesfn$f.contains;
8329elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
8330
8331var cache = function cache(fn, name) {
8332 return function traversalCache(arg1, arg2, arg3, arg4) {
8333 var selectorOrEles = arg1;
8334 var eles = this;
8335 var key;
8336
8337 if (selectorOrEles == null) {
8338 key = '';
8339 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
8340 key = selectorOrEles.id();
8341 }
8342
8343 if (eles.length === 1 && key) {
8344 var _p = eles[0]._private;
8345 var tch = _p.traversalCache = _p.traversalCache || {};
8346 var ch = tch[name] = tch[name] || [];
8347 var hash = hashString(key);
8348 var cacheHit = ch[hash];
8349
8350 if (cacheHit) {
8351 return cacheHit;
8352 } else {
8353 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
8354 }
8355 } else {
8356 return fn.call(eles, arg1, arg2, arg3, arg4);
8357 }
8358 };
8359};
8360
8361var elesfn$g = {
8362 parent: function parent(selector) {
8363 var parents = []; // optimisation for single ele call
8364
8365 if (this.length === 1) {
8366 var parent = this[0]._private.parent;
8367
8368 if (parent) {
8369 return parent;
8370 }
8371 }
8372
8373 for (var i = 0; i < this.length; i++) {
8374 var ele = this[i];
8375 var _parent = ele._private.parent;
8376
8377 if (_parent) {
8378 parents.push(_parent);
8379 }
8380 }
8381
8382 return this.spawn(parents, {
8383 unique: true
8384 }).filter(selector);
8385 },
8386 parents: function parents(selector) {
8387 var parents = [];
8388 var eles = this.parent();
8389
8390 while (eles.nonempty()) {
8391 for (var i = 0; i < eles.length; i++) {
8392 var ele = eles[i];
8393 parents.push(ele);
8394 }
8395
8396 eles = eles.parent();
8397 }
8398
8399 return this.spawn(parents, {
8400 unique: true
8401 }).filter(selector);
8402 },
8403 commonAncestors: function commonAncestors(selector) {
8404 var ancestors;
8405
8406 for (var i = 0; i < this.length; i++) {
8407 var ele = this[i];
8408 var parents = ele.parents();
8409 ancestors = ancestors || parents;
8410 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
8411 }
8412
8413 return ancestors.filter(selector);
8414 },
8415 orphans: function orphans(selector) {
8416 return this.stdFilter(function (ele) {
8417 return ele.isOrphan();
8418 }).filter(selector);
8419 },
8420 nonorphans: function nonorphans(selector) {
8421 return this.stdFilter(function (ele) {
8422 return ele.isChild();
8423 }).filter(selector);
8424 },
8425 children: cache(function (selector) {
8426 var children = [];
8427
8428 for (var i = 0; i < this.length; i++) {
8429 var ele = this[i];
8430 var eleChildren = ele._private.children;
8431
8432 for (var j = 0; j < eleChildren.length; j++) {
8433 children.push(eleChildren[j]);
8434 }
8435 }
8436
8437 return this.spawn(children, {
8438 unique: true
8439 }).filter(selector);
8440 }, 'children'),
8441 siblings: function siblings(selector) {
8442 return this.parent().children().not(this).filter(selector);
8443 },
8444 isParent: function isParent() {
8445 var ele = this[0];
8446
8447 if (ele) {
8448 return ele.isNode() && ele._private.children.length !== 0;
8449 }
8450 },
8451 isChildless: function isChildless() {
8452 var ele = this[0];
8453
8454 if (ele) {
8455 return ele.isNode() && ele._private.children.length === 0;
8456 }
8457 },
8458 isChild: function isChild() {
8459 var ele = this[0];
8460
8461 if (ele) {
8462 return ele.isNode() && ele._private.parent != null;
8463 }
8464 },
8465 isOrphan: function isOrphan() {
8466 var ele = this[0];
8467
8468 if (ele) {
8469 return ele.isNode() && ele._private.parent == null;
8470 }
8471 },
8472 descendants: function descendants(selector) {
8473 var elements = [];
8474
8475 function add(eles) {
8476 for (var i = 0; i < eles.length; i++) {
8477 var ele = eles[i];
8478 elements.push(ele);
8479
8480 if (ele.children().nonempty()) {
8481 add(ele.children());
8482 }
8483 }
8484 }
8485
8486 add(this.children());
8487 return this.spawn(elements, {
8488 unique: true
8489 }).filter(selector);
8490 }
8491};
8492
8493function forEachCompound(eles, fn, includeSelf, recursiveStep) {
8494 var q = [];
8495 var did = new Set$1();
8496 var cy = eles.cy();
8497 var hasCompounds = cy.hasCompoundNodes();
8498
8499 for (var i = 0; i < eles.length; i++) {
8500 var ele = eles[i];
8501
8502 if (includeSelf) {
8503 q.push(ele);
8504 } else if (hasCompounds) {
8505 recursiveStep(q, did, ele);
8506 }
8507 }
8508
8509 while (q.length > 0) {
8510 var _ele = q.shift();
8511
8512 fn(_ele);
8513 did.add(_ele.id());
8514
8515 if (hasCompounds) {
8516 recursiveStep(q, did, _ele);
8517 }
8518 }
8519
8520 return eles;
8521}
8522
8523function addChildren(q, did, ele) {
8524 if (ele.isParent()) {
8525 var children = ele._private.children;
8526
8527 for (var i = 0; i < children.length; i++) {
8528 var child = children[i];
8529
8530 if (!did.has(child.id())) {
8531 q.push(child);
8532 }
8533 }
8534 }
8535} // very efficient version of eles.add( eles.descendants() ).forEach()
8536// for internal use
8537
8538
8539elesfn$g.forEachDown = function (fn) {
8540 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8541 return forEachCompound(this, fn, includeSelf, addChildren);
8542};
8543
8544function addParent(q, did, ele) {
8545 if (ele.isChild()) {
8546 var parent = ele._private.parent;
8547
8548 if (!did.has(parent.id())) {
8549 q.push(parent);
8550 }
8551 }
8552}
8553
8554elesfn$g.forEachUp = function (fn) {
8555 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8556 return forEachCompound(this, fn, includeSelf, addParent);
8557};
8558
8559function addParentAndChildren(q, did, ele) {
8560 addParent(q, did, ele);
8561 addChildren(q, did, ele);
8562}
8563
8564elesfn$g.forEachUpAndDown = function (fn) {
8565 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8566 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
8567}; // aliases
8568
8569
8570elesfn$g.ancestors = elesfn$g.parents;
8571
8572var fn$1, elesfn$h;
8573fn$1 = elesfn$h = {
8574 data: define$3.data({
8575 field: 'data',
8576 bindingEvent: 'data',
8577 allowBinding: true,
8578 allowSetting: true,
8579 settingEvent: 'data',
8580 settingTriggersEvent: true,
8581 triggerFnName: 'trigger',
8582 allowGetting: true,
8583 immutableKeys: {
8584 'id': true,
8585 'source': true,
8586 'target': true,
8587 'parent': true
8588 },
8589 updateStyle: true
8590 }),
8591 removeData: define$3.removeData({
8592 field: 'data',
8593 event: 'data',
8594 triggerFnName: 'trigger',
8595 triggerEvent: true,
8596 immutableKeys: {
8597 'id': true,
8598 'source': true,
8599 'target': true,
8600 'parent': true
8601 },
8602 updateStyle: true
8603 }),
8604 scratch: define$3.data({
8605 field: 'scratch',
8606 bindingEvent: 'scratch',
8607 allowBinding: true,
8608 allowSetting: true,
8609 settingEvent: 'scratch',
8610 settingTriggersEvent: true,
8611 triggerFnName: 'trigger',
8612 allowGetting: true,
8613 updateStyle: true
8614 }),
8615 removeScratch: define$3.removeData({
8616 field: 'scratch',
8617 event: 'scratch',
8618 triggerFnName: 'trigger',
8619 triggerEvent: true,
8620 updateStyle: true
8621 }),
8622 rscratch: define$3.data({
8623 field: 'rscratch',
8624 allowBinding: false,
8625 allowSetting: true,
8626 settingTriggersEvent: false,
8627 allowGetting: true
8628 }),
8629 removeRscratch: define$3.removeData({
8630 field: 'rscratch',
8631 triggerEvent: false
8632 }),
8633 id: function id() {
8634 var ele = this[0];
8635
8636 if (ele) {
8637 return ele._private.data.id;
8638 }
8639 }
8640}; // aliases
8641
8642fn$1.attr = fn$1.data;
8643fn$1.removeAttr = fn$1.removeData;
8644var data$1 = elesfn$h;
8645
8646var elesfn$i = {};
8647
8648function defineDegreeFunction(callback) {
8649 return function (includeLoops) {
8650 var self = this;
8651
8652 if (includeLoops === undefined) {
8653 includeLoops = true;
8654 }
8655
8656 if (self.length === 0) {
8657 return;
8658 }
8659
8660 if (self.isNode() && !self.removed()) {
8661 var degree = 0;
8662 var node = self[0];
8663 var connectedEdges = node._private.edges;
8664
8665 for (var i = 0; i < connectedEdges.length; i++) {
8666 var edge = connectedEdges[i];
8667
8668 if (!includeLoops && edge.isLoop()) {
8669 continue;
8670 }
8671
8672 degree += callback(node, edge);
8673 }
8674
8675 return degree;
8676 } else {
8677 return;
8678 }
8679 };
8680}
8681
8682extend(elesfn$i, {
8683 degree: defineDegreeFunction(function (node, edge) {
8684 if (edge.source().same(edge.target())) {
8685 return 2;
8686 } else {
8687 return 1;
8688 }
8689 }),
8690 indegree: defineDegreeFunction(function (node, edge) {
8691 if (edge.target().same(node)) {
8692 return 1;
8693 } else {
8694 return 0;
8695 }
8696 }),
8697 outdegree: defineDegreeFunction(function (node, edge) {
8698 if (edge.source().same(node)) {
8699 return 1;
8700 } else {
8701 return 0;
8702 }
8703 })
8704});
8705
8706function defineDegreeBoundsFunction(degreeFn, callback) {
8707 return function (includeLoops) {
8708 var ret;
8709 var nodes = this.nodes();
8710
8711 for (var i = 0; i < nodes.length; i++) {
8712 var ele = nodes[i];
8713 var degree = ele[degreeFn](includeLoops);
8714
8715 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
8716 ret = degree;
8717 }
8718 }
8719
8720 return ret;
8721 };
8722}
8723
8724extend(elesfn$i, {
8725 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
8726 return degree < min;
8727 }),
8728 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
8729 return degree > max;
8730 }),
8731 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
8732 return degree < min;
8733 }),
8734 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
8735 return degree > max;
8736 }),
8737 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
8738 return degree < min;
8739 }),
8740 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
8741 return degree > max;
8742 })
8743});
8744extend(elesfn$i, {
8745 totalDegree: function totalDegree(includeLoops) {
8746 var total = 0;
8747 var nodes = this.nodes();
8748
8749 for (var i = 0; i < nodes.length; i++) {
8750 total += nodes[i].degree(includeLoops);
8751 }
8752
8753 return total;
8754 }
8755});
8756
8757var fn$2, elesfn$j;
8758
8759var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
8760 for (var i = 0; i < eles.length; i++) {
8761 var ele = eles[i];
8762
8763 if (!ele.locked()) {
8764 var oldPos = ele._private.position;
8765 var delta = {
8766 x: newPos.x != null ? newPos.x - oldPos.x : 0,
8767 y: newPos.y != null ? newPos.y - oldPos.y : 0
8768 };
8769
8770 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
8771 ele.children().shift(delta, silent);
8772 }
8773
8774 ele.shiftCachedBoundingBox(delta);
8775 }
8776 }
8777};
8778
8779var positionDef = {
8780 field: 'position',
8781 bindingEvent: 'position',
8782 allowBinding: true,
8783 allowSetting: true,
8784 settingEvent: 'position',
8785 settingTriggersEvent: true,
8786 triggerFnName: 'emitAndNotify',
8787 allowGetting: true,
8788 validKeys: ['x', 'y'],
8789 beforeGet: function beforeGet(ele) {
8790 ele.updateCompoundBounds();
8791 },
8792 beforeSet: function beforeSet(eles, newPos) {
8793 beforePositionSet(eles, newPos, false);
8794 },
8795 onSet: function onSet(eles) {
8796 eles.dirtyCompoundBoundsCache();
8797 },
8798 canSet: function canSet(ele) {
8799 return !ele.locked();
8800 }
8801};
8802fn$2 = elesfn$j = {
8803 position: define$3.data(positionDef),
8804 // position but no notification to renderer
8805 silentPosition: define$3.data(extend({}, positionDef, {
8806 allowBinding: false,
8807 allowSetting: true,
8808 settingTriggersEvent: false,
8809 allowGetting: false,
8810 beforeSet: function beforeSet(eles, newPos) {
8811 beforePositionSet(eles, newPos, true);
8812 }
8813 })),
8814 positions: function positions(pos, silent) {
8815 if (plainObject(pos)) {
8816 if (silent) {
8817 this.silentPosition(pos);
8818 } else {
8819 this.position(pos);
8820 }
8821 } else if (fn(pos)) {
8822 var _fn = pos;
8823 var cy = this.cy();
8824 cy.startBatch();
8825
8826 for (var i = 0; i < this.length; i++) {
8827 var ele = this[i];
8828
8829 var _pos = void 0;
8830
8831 if (_pos = _fn(ele, i)) {
8832 if (silent) {
8833 ele.silentPosition(_pos);
8834 } else {
8835 ele.position(_pos);
8836 }
8837 }
8838 }
8839
8840 cy.endBatch();
8841 }
8842
8843 return this; // chaining
8844 },
8845 silentPositions: function silentPositions(pos) {
8846 return this.positions(pos, true);
8847 },
8848 shift: function shift(dim, val, silent) {
8849 var delta;
8850
8851 if (plainObject(dim)) {
8852 delta = {
8853 x: number(dim.x) ? dim.x : 0,
8854 y: number(dim.y) ? dim.y : 0
8855 };
8856 silent = val;
8857 } else if (string(dim) && number(val)) {
8858 delta = {
8859 x: 0,
8860 y: 0
8861 };
8862 delta[dim] = val;
8863 }
8864
8865 if (delta != null) {
8866 var cy = this.cy();
8867 cy.startBatch();
8868
8869 for (var i = 0; i < this.length; i++) {
8870 var ele = this[i];
8871 var pos = ele.position();
8872 var newPos = {
8873 x: pos.x + delta.x,
8874 y: pos.y + delta.y
8875 };
8876
8877 if (silent) {
8878 ele.silentPosition(newPos);
8879 } else {
8880 ele.position(newPos);
8881 }
8882 }
8883
8884 cy.endBatch();
8885 }
8886
8887 return this;
8888 },
8889 silentShift: function silentShift(dim, val) {
8890 if (plainObject(dim)) {
8891 this.shift(dim, true);
8892 } else if (string(dim) && number(val)) {
8893 this.shift(dim, val, true);
8894 }
8895
8896 return this;
8897 },
8898 // get/set the rendered (i.e. on screen) positon of the element
8899 renderedPosition: function renderedPosition(dim, val) {
8900 var ele = this[0];
8901 var cy = this.cy();
8902 var zoom = cy.zoom();
8903 var pan = cy.pan();
8904 var rpos = plainObject(dim) ? dim : undefined;
8905 var setting = rpos !== undefined || val !== undefined && string(dim);
8906
8907 if (ele && ele.isNode()) {
8908 // must have an element and must be a node to return position
8909 if (setting) {
8910 for (var i = 0; i < this.length; i++) {
8911 var _ele = this[i];
8912
8913 if (val !== undefined) {
8914 // set one dimension
8915 _ele.position(dim, (val - pan[dim]) / zoom);
8916 } else if (rpos !== undefined) {
8917 // set whole position
8918 _ele.position(renderedToModelPosition(rpos, zoom, pan));
8919 }
8920 }
8921 } else {
8922 // getting
8923 var pos = ele.position();
8924 rpos = modelToRenderedPosition(pos, zoom, pan);
8925
8926 if (dim === undefined) {
8927 // then return the whole rendered position
8928 return rpos;
8929 } else {
8930 // then return the specified dimension
8931 return rpos[dim];
8932 }
8933 }
8934 } else if (!setting) {
8935 return undefined; // for empty collection case
8936 }
8937
8938 return this; // chaining
8939 },
8940 // get/set the position relative to the parent
8941 relativePosition: function relativePosition(dim, val) {
8942 var ele = this[0];
8943 var cy = this.cy();
8944 var ppos = plainObject(dim) ? dim : undefined;
8945 var setting = ppos !== undefined || val !== undefined && string(dim);
8946 var hasCompoundNodes = cy.hasCompoundNodes();
8947
8948 if (ele && ele.isNode()) {
8949 // must have an element and must be a node to return position
8950 if (setting) {
8951 for (var i = 0; i < this.length; i++) {
8952 var _ele2 = this[i];
8953 var parent = hasCompoundNodes ? _ele2.parent() : null;
8954 var hasParent = parent && parent.length > 0;
8955 var relativeToParent = hasParent;
8956
8957 if (hasParent) {
8958 parent = parent[0];
8959 }
8960
8961 var origin = relativeToParent ? parent.position() : {
8962 x: 0,
8963 y: 0
8964 };
8965
8966 if (val !== undefined) {
8967 // set one dimension
8968 _ele2.position(dim, val + origin[dim]);
8969 } else if (ppos !== undefined) {
8970 // set whole position
8971 _ele2.position({
8972 x: ppos.x + origin.x,
8973 y: ppos.y + origin.y
8974 });
8975 }
8976 }
8977 } else {
8978 // getting
8979 var pos = ele.position();
8980
8981 var _parent = hasCompoundNodes ? ele.parent() : null;
8982
8983 var _hasParent = _parent && _parent.length > 0;
8984
8985 var _relativeToParent = _hasParent;
8986
8987 if (_hasParent) {
8988 _parent = _parent[0];
8989 }
8990
8991 var _origin = _relativeToParent ? _parent.position() : {
8992 x: 0,
8993 y: 0
8994 };
8995
8996 ppos = {
8997 x: pos.x - _origin.x,
8998 y: pos.y - _origin.y
8999 };
9000
9001 if (dim === undefined) {
9002 // then return the whole rendered position
9003 return ppos;
9004 } else {
9005 // then return the specified dimension
9006 return ppos[dim];
9007 }
9008 }
9009 } else if (!setting) {
9010 return undefined; // for empty collection case
9011 }
9012
9013 return this; // chaining
9014 }
9015}; // aliases
9016
9017fn$2.modelPosition = fn$2.point = fn$2.position;
9018fn$2.modelPositions = fn$2.points = fn$2.positions;
9019fn$2.renderedPoint = fn$2.renderedPosition;
9020fn$2.relativePoint = fn$2.relativePosition;
9021var position = elesfn$j;
9022
9023var fn$3, elesfn$k;
9024fn$3 = elesfn$k = {};
9025
9026elesfn$k.renderedBoundingBox = function (options) {
9027 var bb = this.boundingBox(options);
9028 var cy = this.cy();
9029 var zoom = cy.zoom();
9030 var pan = cy.pan();
9031 var x1 = bb.x1 * zoom + pan.x;
9032 var x2 = bb.x2 * zoom + pan.x;
9033 var y1 = bb.y1 * zoom + pan.y;
9034 var y2 = bb.y2 * zoom + pan.y;
9035 return {
9036 x1: x1,
9037 x2: x2,
9038 y1: y1,
9039 y2: y2,
9040 w: x2 - x1,
9041 h: y2 - y1
9042 };
9043};
9044
9045elesfn$k.dirtyCompoundBoundsCache = function () {
9046 var cy = this.cy();
9047
9048 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9049 return this;
9050 }
9051
9052 this.forEachUp(function (ele) {
9053 if (ele.isParent()) {
9054 var _p = ele._private;
9055 _p.compoundBoundsClean = false;
9056 _p.bbCache = null;
9057 ele.emitAndNotify('bounds');
9058 }
9059 });
9060 return this;
9061};
9062
9063elesfn$k.updateCompoundBounds = function () {
9064 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9065 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9066
9067 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9068 return this;
9069 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9070
9071
9072 if (!force && cy.batching()) {
9073 return this;
9074 }
9075
9076 function update(parent) {
9077 if (!parent.isParent()) {
9078 return;
9079 }
9080
9081 var _p = parent._private;
9082 var children = parent.children();
9083 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9084 var min = {
9085 width: {
9086 val: parent.pstyle('min-width').pfValue,
9087 left: parent.pstyle('min-width-bias-left'),
9088 right: parent.pstyle('min-width-bias-right')
9089 },
9090 height: {
9091 val: parent.pstyle('min-height').pfValue,
9092 top: parent.pstyle('min-height-bias-top'),
9093 bottom: parent.pstyle('min-height-bias-bottom')
9094 }
9095 };
9096 var bb = children.boundingBox({
9097 includeLabels: includeLabels,
9098 includeOverlays: false,
9099 // updating the compound bounds happens outside of the regular
9100 // cache cycle (i.e. before fired events)
9101 useCache: false
9102 });
9103 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9104
9105 if (bb.w === 0 || bb.h === 0) {
9106 bb = {
9107 w: parent.pstyle('width').pfValue,
9108 h: parent.pstyle('height').pfValue
9109 };
9110 bb.x1 = pos.x - bb.w / 2;
9111 bb.x2 = pos.x + bb.w / 2;
9112 bb.y1 = pos.y - bb.h / 2;
9113 bb.y2 = pos.y + bb.h / 2;
9114 }
9115
9116 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9117 var biasDiff = 0;
9118 var biasComplementDiff = 0;
9119 var biasTotal = propBias + propBiasComplement;
9120
9121 if (propDiff > 0 && biasTotal > 0) {
9122 biasDiff = propBias / biasTotal * propDiff;
9123 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9124 }
9125
9126 return {
9127 biasDiff: biasDiff,
9128 biasComplementDiff: biasComplementDiff
9129 };
9130 }
9131
9132 function computePaddingValues(width, height, paddingObject, relativeTo) {
9133 // Assuming percentage is number from 0 to 1
9134 if (paddingObject.units === '%') {
9135 switch (relativeTo) {
9136 case 'width':
9137 return width > 0 ? paddingObject.pfValue * width : 0;
9138
9139 case 'height':
9140 return height > 0 ? paddingObject.pfValue * height : 0;
9141
9142 case 'average':
9143 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9144
9145 case 'min':
9146 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9147
9148 case 'max':
9149 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9150
9151 default:
9152 return 0;
9153 }
9154 } else if (paddingObject.units === 'px') {
9155 return paddingObject.pfValue;
9156 } else {
9157 return 0;
9158 }
9159 }
9160
9161 var leftVal = min.width.left.value;
9162
9163 if (min.width.left.units === 'px' && min.width.val > 0) {
9164 leftVal = leftVal * 100 / min.width.val;
9165 }
9166
9167 var rightVal = min.width.right.value;
9168
9169 if (min.width.right.units === 'px' && min.width.val > 0) {
9170 rightVal = rightVal * 100 / min.width.val;
9171 }
9172
9173 var topVal = min.height.top.value;
9174
9175 if (min.height.top.units === 'px' && min.height.val > 0) {
9176 topVal = topVal * 100 / min.height.val;
9177 }
9178
9179 var bottomVal = min.height.bottom.value;
9180
9181 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9182 bottomVal = bottomVal * 100 / min.height.val;
9183 }
9184
9185 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9186 var diffLeft = widthBiasDiffs.biasDiff;
9187 var diffRight = widthBiasDiffs.biasComplementDiff;
9188 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9189 var diffTop = heightBiasDiffs.biasDiff;
9190 var diffBottom = heightBiasDiffs.biasComplementDiff;
9191 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9192 _p.autoWidth = Math.max(bb.w, min.width.val);
9193 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9194 _p.autoHeight = Math.max(bb.h, min.height.val);
9195 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9196 }
9197
9198 for (var i = 0; i < this.length; i++) {
9199 var ele = this[i];
9200 var _p = ele._private;
9201
9202 if (!_p.compoundBoundsClean) {
9203 update(ele);
9204
9205 if (!cy.batching()) {
9206 _p.compoundBoundsClean = true;
9207 }
9208 }
9209 }
9210
9211 return this;
9212};
9213
9214var noninf = function noninf(x) {
9215 if (x === Infinity || x === -Infinity) {
9216 return 0;
9217 }
9218
9219 return x;
9220};
9221
9222var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9223 // don't update with zero area boxes
9224 if (x2 - x1 === 0 || y2 - y1 === 0) {
9225 return;
9226 } // don't update with null dim
9227
9228
9229 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9230 return;
9231 }
9232
9233 b.x1 = x1 < b.x1 ? x1 : b.x1;
9234 b.x2 = x2 > b.x2 ? x2 : b.x2;
9235 b.y1 = y1 < b.y1 ? y1 : b.y1;
9236 b.y2 = y2 > b.y2 ? y2 : b.y2;
9237 b.w = b.x2 - b.x1;
9238 b.h = b.y2 - b.y1;
9239};
9240
9241var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
9242 if (b2 == null) {
9243 return b;
9244 }
9245
9246 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
9247};
9248
9249var prefixedProperty = function prefixedProperty(obj, field, prefix) {
9250 return getPrefixedProperty(obj, field, prefix);
9251};
9252
9253var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
9254 if (ele.cy().headless()) {
9255 return;
9256 }
9257
9258 var _p = ele._private;
9259 var rstyle = _p.rstyle;
9260 var halfArW = rstyle.arrowWidth / 2;
9261 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
9262 var x;
9263 var y;
9264
9265 if (arrowType !== 'none') {
9266 if (prefix === 'source') {
9267 x = rstyle.srcX;
9268 y = rstyle.srcY;
9269 } else if (prefix === 'target') {
9270 x = rstyle.tgtX;
9271 y = rstyle.tgtY;
9272 } else {
9273 x = rstyle.midX;
9274 y = rstyle.midY;
9275 } // always store the individual arrow bounds
9276
9277
9278 var bbs = _p.arrowBounds = _p.arrowBounds || {};
9279 var bb = bbs[prefix] = bbs[prefix] || {};
9280 bb.x1 = x - halfArW;
9281 bb.y1 = y - halfArW;
9282 bb.x2 = x + halfArW;
9283 bb.y2 = y + halfArW;
9284 bb.w = bb.x2 - bb.x1;
9285 bb.h = bb.y2 - bb.y1;
9286 expandBoundingBox(bb, 1);
9287 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
9288 }
9289};
9290
9291var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
9292 if (ele.cy().headless()) {
9293 return;
9294 }
9295
9296 var prefixDash;
9297
9298 if (prefix) {
9299 prefixDash = prefix + '-';
9300 } else {
9301 prefixDash = '';
9302 }
9303
9304 var _p = ele._private;
9305 var rstyle = _p.rstyle;
9306 var label = ele.pstyle(prefixDash + 'label').strValue;
9307
9308 if (label) {
9309 var halign = ele.pstyle('text-halign');
9310 var valign = ele.pstyle('text-valign');
9311 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
9312 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
9313 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
9314 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
9315 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
9316 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
9317 var isEdge = ele.isEdge();
9318 var rotation = ele.pstyle(prefixDash + 'text-rotation');
9319 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
9320 var borderWidth = ele.pstyle('text-border-width').pfValue;
9321 var halfBorderWidth = borderWidth / 2;
9322 var padding = ele.pstyle('text-background-padding').pfValue;
9323 var lh = labelHeight;
9324 var lw = labelWidth;
9325 var lw_2 = lw / 2;
9326 var lh_2 = lh / 2;
9327 var lx1, lx2, ly1, ly2;
9328
9329 if (isEdge) {
9330 lx1 = labelX - lw_2;
9331 lx2 = labelX + lw_2;
9332 ly1 = labelY - lh_2;
9333 ly2 = labelY + lh_2;
9334 } else {
9335 switch (halign.value) {
9336 case 'left':
9337 lx1 = labelX - lw;
9338 lx2 = labelX;
9339 break;
9340
9341 case 'center':
9342 lx1 = labelX - lw_2;
9343 lx2 = labelX + lw_2;
9344 break;
9345
9346 case 'right':
9347 lx1 = labelX;
9348 lx2 = labelX + lw;
9349 break;
9350 }
9351
9352 switch (valign.value) {
9353 case 'top':
9354 ly1 = labelY - lh;
9355 ly2 = labelY;
9356 break;
9357
9358 case 'center':
9359 ly1 = labelY - lh_2;
9360 ly2 = labelY + lh_2;
9361 break;
9362
9363 case 'bottom':
9364 ly1 = labelY;
9365 ly2 = labelY + lh;
9366 break;
9367 }
9368 } // shift by margin and expand by outline and border
9369
9370
9371 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding;
9372 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding;
9373 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding;
9374 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding; // always store the unrotated label bounds separately
9375
9376 var bbPrefix = prefix || 'main';
9377 var bbs = _p.labelBounds;
9378 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
9379 bb.x1 = lx1;
9380 bb.y1 = ly1;
9381 bb.x2 = lx2;
9382 bb.y2 = ly2;
9383 bb.w = lx2 - lx1;
9384 bb.h = ly2 - ly1;
9385 expandBoundingBox(bb, 1); // expand to work around browser dimension inaccuracies
9386
9387 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
9388 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
9389
9390 if (isAutorotate || isPfValue) {
9391 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
9392 var cos = Math.cos(theta);
9393 var sin = Math.sin(theta); // rotation point (default value for center-center)
9394
9395 var xo = (lx1 + lx2) / 2;
9396 var yo = (ly1 + ly2) / 2;
9397
9398 if (!isEdge) {
9399 switch (halign.value) {
9400 case 'left':
9401 xo = lx2;
9402 break;
9403
9404 case 'right':
9405 xo = lx1;
9406 break;
9407 }
9408
9409 switch (valign.value) {
9410 case 'top':
9411 yo = ly2;
9412 break;
9413
9414 case 'bottom':
9415 yo = ly1;
9416 break;
9417 }
9418 }
9419
9420 var rotate = function rotate(x, y) {
9421 x = x - xo;
9422 y = y - yo;
9423 return {
9424 x: x * cos - y * sin + xo,
9425 y: x * sin + y * cos + yo
9426 };
9427 };
9428
9429 var px1y1 = rotate(lx1, ly1);
9430 var px1y2 = rotate(lx1, ly2);
9431 var px2y1 = rotate(lx2, ly1);
9432 var px2y2 = rotate(lx2, ly2);
9433 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9434 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9435 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9436 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9437 }
9438
9439 var bbPrefixRot = bbPrefix + 'Rot';
9440 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
9441 bbRot.x1 = lx1;
9442 bbRot.y1 = ly1;
9443 bbRot.x2 = lx2;
9444 bbRot.y2 = ly2;
9445 bbRot.w = lx2 - lx1;
9446 bbRot.h = ly2 - ly1;
9447 updateBounds(bounds, lx1, ly1, lx2, ly2);
9448 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
9449 }
9450
9451 return bounds;
9452}; // get the bounding box of the elements (in raw model position)
9453
9454
9455var boundingBoxImpl = function boundingBoxImpl(ele, options) {
9456 var cy = ele._private.cy;
9457 var styleEnabled = cy.styleEnabled();
9458 var headless = cy.headless();
9459 var bounds = makeBoundingBox();
9460 var _p = ele._private;
9461 var isNode = ele.isNode();
9462 var isEdge = ele.isEdge();
9463 var ex1, ex2, ey1, ey2; // extrema of body / lines
9464
9465 var x, y; // node pos
9466
9467 var rstyle = _p.rstyle;
9468 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
9469 // (other factors like width values will be considered later in this function anyway)
9470
9471 var isDisplayed = function isDisplayed(ele) {
9472 return ele.pstyle('display').value !== 'none';
9473 };
9474
9475 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
9476 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
9477
9478 if (displayed) {
9479 // displayed suffices, since we will find zero area eles anyway
9480 var overlayOpacity = 0;
9481 var overlayPadding = 0;
9482
9483 if (styleEnabled && options.includeOverlays) {
9484 overlayOpacity = ele.pstyle('overlay-opacity').value;
9485
9486 if (overlayOpacity !== 0) {
9487 overlayPadding = ele.pstyle('overlay-padding').value;
9488 }
9489 }
9490
9491 var w = 0;
9492 var wHalf = 0;
9493
9494 if (styleEnabled) {
9495 w = ele.pstyle('width').pfValue;
9496 wHalf = w / 2;
9497 }
9498
9499 if (isNode && options.includeNodes) {
9500 var pos = ele.position();
9501 x = pos.x;
9502 y = pos.y;
9503
9504 var _w = ele.outerWidth();
9505
9506 var halfW = _w / 2;
9507 var h = ele.outerHeight();
9508 var halfH = h / 2; // handle node dimensions
9509 /////////////////////////
9510
9511 ex1 = x - halfW;
9512 ex2 = x + halfW;
9513 ey1 = y - halfH;
9514 ey2 = y + halfH;
9515 updateBounds(bounds, ex1, ey1, ex2, ey2);
9516 } else if (isEdge && options.includeEdges) {
9517 if (styleEnabled && !headless) {
9518 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
9519 //////////////////////////////////////////////
9520
9521 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9522 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9523 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
9524 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
9525
9526 ex1 -= wHalf;
9527 ex2 += wHalf;
9528 ey1 -= wHalf;
9529 ey2 += wHalf;
9530 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
9531 ////////////////
9532
9533 if (curveStyle === 'haystack') {
9534 var hpts = rstyle.haystackPts;
9535
9536 if (hpts && hpts.length === 2) {
9537 ex1 = hpts[0].x;
9538 ey1 = hpts[0].y;
9539 ex2 = hpts[1].x;
9540 ey2 = hpts[1].y;
9541
9542 if (ex1 > ex2) {
9543 var temp = ex1;
9544 ex1 = ex2;
9545 ex2 = temp;
9546 }
9547
9548 if (ey1 > ey2) {
9549 var _temp = ey1;
9550 ey1 = ey2;
9551 ey2 = _temp;
9552 }
9553
9554 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
9555 }
9556 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
9557 var pts;
9558
9559 switch (curveStyle) {
9560 case 'bezier':
9561 case 'unbundled-bezier':
9562 pts = rstyle.bezierPts;
9563 break;
9564
9565 case 'segments':
9566 case 'taxi':
9567 pts = rstyle.linePts;
9568 break;
9569 }
9570
9571 if (pts != null) {
9572 for (var j = 0; j < pts.length; j++) {
9573 var pt = pts[j];
9574 ex1 = pt.x - wHalf;
9575 ex2 = pt.x + wHalf;
9576 ey1 = pt.y - wHalf;
9577 ey2 = pt.y + wHalf;
9578 updateBounds(bounds, ex1, ey1, ex2, ey2);
9579 }
9580 }
9581 } // bezier-like or segment-like edge
9582
9583 } else {
9584 // headless or style disabled
9585 // fallback on source and target positions
9586 //////////////////////////////////////////
9587 var n1 = ele.source();
9588 var n1pos = n1.position();
9589 var n2 = ele.target();
9590 var n2pos = n2.position();
9591 ex1 = n1pos.x;
9592 ex2 = n2pos.x;
9593 ey1 = n1pos.y;
9594 ey2 = n2pos.y;
9595
9596 if (ex1 > ex2) {
9597 var _temp2 = ex1;
9598 ex1 = ex2;
9599 ex2 = _temp2;
9600 }
9601
9602 if (ey1 > ey2) {
9603 var _temp3 = ey1;
9604 ey1 = ey2;
9605 ey2 = _temp3;
9606 } // take into account edge width
9607
9608
9609 ex1 -= wHalf;
9610 ex2 += wHalf;
9611 ey1 -= wHalf;
9612 ey2 += wHalf;
9613 updateBounds(bounds, ex1, ey1, ex2, ey2);
9614 } // headless or style disabled
9615
9616 } // edges
9617 // handle edge arrow size
9618 /////////////////////////
9619
9620
9621 if (styleEnabled && options.includeEdges && isEdge) {
9622 updateBoundsFromArrow(bounds, ele, 'mid-source');
9623 updateBoundsFromArrow(bounds, ele, 'mid-target');
9624 updateBoundsFromArrow(bounds, ele, 'source');
9625 updateBoundsFromArrow(bounds, ele, 'target');
9626 } // ghost
9627 ////////
9628
9629
9630 if (styleEnabled) {
9631 var ghost = ele.pstyle('ghost').value === 'yes';
9632
9633 if (ghost) {
9634 var gx = ele.pstyle('ghost-offset-x').pfValue;
9635 var gy = ele.pstyle('ghost-offset-y').pfValue;
9636 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
9637 }
9638 } // always store the body bounds separately from the labels
9639
9640
9641 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
9642 assignBoundingBox(bbBody, bounds);
9643 expandBoundingBoxSides(bbBody, manualExpansion);
9644 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
9645 // overlay
9646 //////////
9647
9648 if (styleEnabled) {
9649 ex1 = bounds.x1;
9650 ex2 = bounds.x2;
9651 ey1 = bounds.y1;
9652 ey2 = bounds.y2;
9653 updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
9654 } // always store the body bounds separately from the labels
9655
9656
9657 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
9658 assignBoundingBox(bbOverlay, bounds);
9659 expandBoundingBoxSides(bbOverlay, manualExpansion);
9660 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
9661 // handle label dimensions
9662 //////////////////////////
9663
9664 var bbLabels = _p.labelBounds = _p.labelBounds || {};
9665
9666 if (bbLabels.all != null) {
9667 clearBoundingBox(bbLabels.all);
9668 } else {
9669 bbLabels.all = makeBoundingBox();
9670 }
9671
9672 if (styleEnabled && options.includeLabels) {
9673 if (options.includeMainLabels) {
9674 updateBoundsFromLabel(bounds, ele, null);
9675 }
9676
9677 if (isEdge) {
9678 if (options.includeSourceLabels) {
9679 updateBoundsFromLabel(bounds, ele, 'source');
9680 }
9681
9682 if (options.includeTargetLabels) {
9683 updateBoundsFromLabel(bounds, ele, 'target');
9684 }
9685 }
9686 } // style enabled for labels
9687
9688 } // if displayed
9689
9690
9691 bounds.x1 = noninf(bounds.x1);
9692 bounds.y1 = noninf(bounds.y1);
9693 bounds.x2 = noninf(bounds.x2);
9694 bounds.y2 = noninf(bounds.y2);
9695 bounds.w = noninf(bounds.x2 - bounds.x1);
9696 bounds.h = noninf(bounds.y2 - bounds.y1);
9697
9698 if (bounds.w > 0 && bounds.h > 0 && displayed) {
9699 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
9700
9701 expandBoundingBox(bounds, 1);
9702 }
9703
9704 return bounds;
9705};
9706
9707var getKey = function getKey(opts) {
9708 var i = 0;
9709
9710 var tf = function tf(val) {
9711 return (val ? 1 : 0) << i++;
9712 };
9713
9714 var key = 0;
9715 key += tf(opts.incudeNodes);
9716 key += tf(opts.includeEdges);
9717 key += tf(opts.includeLabels);
9718 key += tf(opts.includeMainLabels);
9719 key += tf(opts.includeSourceLabels);
9720 key += tf(opts.includeTargetLabels);
9721 key += tf(opts.includeOverlays);
9722 return key;
9723};
9724
9725var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
9726 if (ele.isEdge()) {
9727 var p1 = ele.source().position();
9728 var p2 = ele.target().position();
9729
9730 var r = function r(x) {
9731 return Math.round(x);
9732 };
9733
9734 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
9735 } else {
9736 return 0;
9737 }
9738};
9739
9740var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
9741 var _p = ele._private;
9742 var bb;
9743 var isEdge = ele.isEdge();
9744 var key = opts == null ? defBbOptsKey : getKey(opts);
9745 var usingDefOpts = key === defBbOptsKey;
9746 var currPosKey = getBoundingBoxPosKey(ele);
9747 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9748 var useCache = opts.useCache && isPosKeySame;
9749
9750 var isDirty = function isDirty(ele) {
9751 return ele._private.bbCache == null;
9752 };
9753
9754 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
9755
9756 if (needRecalc) {
9757 if (!isPosKeySame) {
9758 ele.recalculateRenderedStyle();
9759 }
9760
9761 bb = boundingBoxImpl(ele, defBbOpts);
9762 _p.bbCache = bb;
9763 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
9764 _p.bbCachePosKey = currPosKey;
9765 } else {
9766 bb = _p.bbCache;
9767 }
9768
9769 if (!needRecalc && (_p.bbCacheShift.x !== 0 || _p.bbCacheShift.y !== 0)) {
9770 var shift = assignShiftToBoundingBox;
9771 var delta = _p.bbCacheShift;
9772
9773 var safeShift = function safeShift(bb, delta) {
9774 if (bb != null) {
9775 shift(bb, delta);
9776 }
9777 };
9778
9779 shift(bb, delta);
9780 var bodyBounds = _p.bodyBounds,
9781 overlayBounds = _p.overlayBounds,
9782 labelBounds = _p.labelBounds,
9783 arrowBounds = _p.arrowBounds;
9784 safeShift(bodyBounds, delta);
9785 safeShift(overlayBounds, delta);
9786
9787 if (arrowBounds != null) {
9788 safeShift(arrowBounds.source, delta);
9789 safeShift(arrowBounds.target, delta);
9790 safeShift(arrowBounds['mid-source'], delta);
9791 safeShift(arrowBounds['mid-target'], delta);
9792 }
9793
9794 if (labelBounds != null) {
9795 safeShift(labelBounds.main, delta);
9796 safeShift(labelBounds.all, delta);
9797 safeShift(labelBounds.source, delta);
9798 safeShift(labelBounds.target, delta);
9799 }
9800 } // always reset the shift, because we either applied the shift or cleared it by doing a fresh recalc
9801
9802
9803 _p.bbCacheShift.x = _p.bbCacheShift.y = 0; // not using def opts => need to build up bb from combination of sub bbs
9804
9805 if (!usingDefOpts) {
9806 var isNode = ele.isNode();
9807 bb = makeBoundingBox();
9808
9809 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
9810 if (opts.includeOverlays) {
9811 updateBoundsFromBox(bb, _p.overlayBounds);
9812 } else {
9813 updateBoundsFromBox(bb, _p.bodyBounds);
9814 }
9815 }
9816
9817 if (opts.includeLabels) {
9818 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
9819 updateBoundsFromBox(bb, _p.labelBounds.all);
9820 } else {
9821 if (opts.includeMainLabels) {
9822 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
9823 }
9824
9825 if (opts.includeSourceLabels) {
9826 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
9827 }
9828
9829 if (opts.includeTargetLabels) {
9830 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
9831 }
9832 }
9833 }
9834
9835 bb.w = bb.x2 - bb.x1;
9836 bb.h = bb.y2 - bb.y1;
9837 }
9838
9839 return bb;
9840};
9841
9842var defBbOpts = {
9843 includeNodes: true,
9844 includeEdges: true,
9845 includeLabels: true,
9846 includeMainLabels: true,
9847 includeSourceLabels: true,
9848 includeTargetLabels: true,
9849 includeOverlays: true,
9850 useCache: true
9851};
9852var defBbOptsKey = getKey(defBbOpts);
9853var filledBbOpts = defaults(defBbOpts);
9854
9855elesfn$k.boundingBox = function (options) {
9856 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
9857 // specified s.t. the cache is used, so check for this case to make it faster by
9858 // avoiding the overhead of the rest of the function
9859
9860 if (this.length === 1 && this[0]._private.bbCache != null && (options === undefined || options.useCache === undefined || options.useCache === true)) {
9861 if (options === undefined) {
9862 options = defBbOpts;
9863 } else {
9864 options = filledBbOpts(options);
9865 }
9866
9867 bounds = cachedBoundingBoxImpl(this[0], options);
9868 } else {
9869 bounds = makeBoundingBox();
9870 options = options || defBbOpts;
9871 var opts = filledBbOpts(options);
9872 var eles = this;
9873 var cy = eles.cy();
9874 var styleEnabled = cy.styleEnabled();
9875
9876 if (styleEnabled) {
9877 for (var i = 0; i < eles.length; i++) {
9878 var ele = eles[i];
9879 var _p = ele._private;
9880 var currPosKey = getBoundingBoxPosKey(ele);
9881 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9882 var useCache = opts.useCache && isPosKeySame;
9883 ele.recalculateRenderedStyle(useCache);
9884 }
9885 }
9886
9887 this.updateCompoundBounds();
9888
9889 for (var _i = 0; _i < eles.length; _i++) {
9890 var _ele = eles[_i];
9891 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
9892 }
9893 }
9894
9895 bounds.x1 = noninf(bounds.x1);
9896 bounds.y1 = noninf(bounds.y1);
9897 bounds.x2 = noninf(bounds.x2);
9898 bounds.y2 = noninf(bounds.y2);
9899 bounds.w = noninf(bounds.x2 - bounds.x1);
9900 bounds.h = noninf(bounds.y2 - bounds.y1);
9901 return bounds;
9902};
9903
9904elesfn$k.dirtyBoundingBoxCache = function () {
9905 for (var i = 0; i < this.length; i++) {
9906 var _p = this[i]._private;
9907 _p.bbCache = null;
9908 _p.bbCacheShift.x = _p.bbCacheShift.y = 0;
9909 _p.bbCachePosKey = null;
9910 _p.bodyBounds = null;
9911 _p.overlayBounds = null;
9912 _p.labelBounds.all = null;
9913 _p.labelBounds.source = null;
9914 _p.labelBounds.target = null;
9915 _p.labelBounds.main = null;
9916 _p.labelBounds.sourceRot = null;
9917 _p.labelBounds.targetRot = null;
9918 _p.labelBounds.mainRot = null;
9919 _p.arrowBounds.source = null;
9920 _p.arrowBounds.target = null;
9921 _p.arrowBounds['mid-source'] = null;
9922 _p.arrowBounds['mid-target'] = null;
9923 }
9924
9925 this.emitAndNotify('bounds');
9926 return this;
9927};
9928
9929elesfn$k.shiftCachedBoundingBox = function (delta) {
9930 for (var i = 0; i < this.length; i++) {
9931 var ele = this[i];
9932 var _p = ele._private;
9933 var bb = _p.bbCache;
9934
9935 if (bb != null) {
9936 _p.bbCacheShift.x += delta.x;
9937 _p.bbCacheShift.y += delta.y;
9938 }
9939 }
9940
9941 this.emitAndNotify('bounds');
9942 return this;
9943}; // private helper to get bounding box for custom node positions
9944// - good for perf in certain cases but currently requires dirtying the rendered style
9945// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
9946// - try to use for only things like discrete layouts where the node position would change anyway
9947
9948
9949elesfn$k.boundingBoxAt = function (fn) {
9950 var nodes = this.nodes();
9951 var cy = this.cy();
9952 var hasCompoundNodes = cy.hasCompoundNodes();
9953
9954 if (hasCompoundNodes) {
9955 nodes = nodes.filter(function (node) {
9956 return !node.isParent();
9957 });
9958 }
9959
9960 if (plainObject(fn)) {
9961 var obj = fn;
9962
9963 fn = function fn() {
9964 return obj;
9965 };
9966 }
9967
9968 var storeOldPos = function storeOldPos(node, i) {
9969 return node._private.bbAtOldPos = fn(node, i);
9970 };
9971
9972 var getOldPos = function getOldPos(node) {
9973 return node._private.bbAtOldPos;
9974 };
9975
9976 cy.startBatch();
9977 nodes.forEach(storeOldPos).silentPositions(fn);
9978
9979 if (hasCompoundNodes) {
9980 this.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
9981 }
9982
9983 var bb = copyBoundingBox(this.boundingBox({
9984 useCache: false
9985 }));
9986 nodes.silentPositions(getOldPos);
9987 cy.endBatch();
9988 return bb;
9989};
9990
9991fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
9992fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
9993var bounds = elesfn$k;
9994
9995var fn$4, elesfn$l;
9996fn$4 = elesfn$l = {};
9997
9998var defineDimFns = function defineDimFns(opts) {
9999 opts.uppercaseName = capitalize(opts.name);
10000 opts.autoName = 'auto' + opts.uppercaseName;
10001 opts.labelName = 'label' + opts.uppercaseName;
10002 opts.outerName = 'outer' + opts.uppercaseName;
10003 opts.uppercaseOuterName = capitalize(opts.outerName);
10004
10005 fn$4[opts.name] = function dimImpl() {
10006 var ele = this[0];
10007 var _p = ele._private;
10008 var cy = _p.cy;
10009 var styleEnabled = cy._private.styleEnabled;
10010
10011 if (ele) {
10012 if (styleEnabled) {
10013 if (ele.isParent()) {
10014 ele.updateCompoundBounds();
10015 return _p[opts.autoName] || 0;
10016 }
10017
10018 var d = ele.pstyle(opts.name);
10019
10020 switch (d.strValue) {
10021 case 'label':
10022 ele.recalculateRenderedStyle();
10023 return _p.rstyle[opts.labelName] || 0;
10024
10025 default:
10026 return d.pfValue;
10027 }
10028 } else {
10029 return 1;
10030 }
10031 }
10032 };
10033
10034 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
10035 var ele = this[0];
10036 var _p = ele._private;
10037 var cy = _p.cy;
10038 var styleEnabled = cy._private.styleEnabled;
10039
10040 if (ele) {
10041 if (styleEnabled) {
10042 var dim = ele[opts.name]();
10043 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10044
10045 var padding = 2 * ele.padding();
10046 return dim + border + padding;
10047 } else {
10048 return 1;
10049 }
10050 }
10051 };
10052
10053 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10054 var ele = this[0];
10055
10056 if (ele) {
10057 var d = ele[opts.name]();
10058 return d * this.cy().zoom();
10059 }
10060 };
10061
10062 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10063 var ele = this[0];
10064
10065 if (ele) {
10066 var od = ele[opts.outerName]();
10067 return od * this.cy().zoom();
10068 }
10069 };
10070};
10071
10072defineDimFns({
10073 name: 'width'
10074});
10075defineDimFns({
10076 name: 'height'
10077});
10078
10079elesfn$l.padding = function () {
10080 var ele = this[0];
10081 var _p = ele._private;
10082
10083 if (ele.isParent()) {
10084 ele.updateCompoundBounds();
10085
10086 if (_p.autoPadding !== undefined) {
10087 return _p.autoPadding;
10088 } else {
10089 return ele.pstyle('padding').pfValue;
10090 }
10091 } else {
10092 return ele.pstyle('padding').pfValue;
10093 }
10094};
10095
10096elesfn$l.paddedHeight = function () {
10097 var ele = this[0];
10098 return ele.height() + 2 * ele.padding();
10099};
10100
10101elesfn$l.paddedWidth = function () {
10102 var ele = this[0];
10103 return ele.width() + 2 * ele.padding();
10104};
10105
10106var widthHeight = elesfn$l;
10107
10108var ifEdge = function ifEdge(ele, getValue) {
10109 if (ele.isEdge()) {
10110 return getValue(ele);
10111 }
10112};
10113
10114var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10115 if (ele.isEdge()) {
10116 var cy = ele.cy();
10117 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10118 }
10119};
10120
10121var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10122 if (ele.isEdge()) {
10123 var cy = ele.cy();
10124 var pan = cy.pan();
10125 var zoom = cy.zoom();
10126 return getPoints(ele).map(function (p) {
10127 return modelToRenderedPosition(p, zoom, pan);
10128 });
10129 }
10130};
10131
10132var controlPoints = function controlPoints(ele) {
10133 return ele.renderer().getControlPoints(ele);
10134};
10135
10136var segmentPoints = function segmentPoints(ele) {
10137 return ele.renderer().getSegmentPoints(ele);
10138};
10139
10140var sourceEndpoint = function sourceEndpoint(ele) {
10141 return ele.renderer().getSourceEndpoint(ele);
10142};
10143
10144var targetEndpoint = function targetEndpoint(ele) {
10145 return ele.renderer().getTargetEndpoint(ele);
10146};
10147
10148var midpoint = function midpoint(ele) {
10149 return ele.renderer().getEdgeMidpoint(ele);
10150};
10151
10152var pts = {
10153 controlPoints: {
10154 get: controlPoints,
10155 mult: true
10156 },
10157 segmentPoints: {
10158 get: segmentPoints,
10159 mult: true
10160 },
10161 sourceEndpoint: {
10162 get: sourceEndpoint
10163 },
10164 targetEndpoint: {
10165 get: targetEndpoint
10166 },
10167 midpoint: {
10168 get: midpoint
10169 }
10170};
10171
10172var renderedName = function renderedName(name) {
10173 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10174};
10175
10176var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10177 var spec = pts[name];
10178 var rName = renderedName(name);
10179
10180 obj[name] = function () {
10181 return ifEdge(this, spec.get);
10182 };
10183
10184 if (spec.mult) {
10185 obj[rName] = function () {
10186 return ifEdgeRenderedPositions(this, spec.get);
10187 };
10188 } else {
10189 obj[rName] = function () {
10190 return ifEdgeRenderedPosition(this, spec.get);
10191 };
10192 }
10193
10194 return obj;
10195}, {});
10196
10197var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10198
10199/*!
10200Event object based on jQuery events, MIT license
10201
10202https://jquery.org/license/
10203https://tldrlegal.com/license/mit-license
10204https://github.com/jquery/jquery/blob/master/src/event.js
10205*/
10206var Event = function Event(src, props) {
10207 this.recycle(src, props);
10208};
10209
10210function returnFalse() {
10211 return false;
10212}
10213
10214function returnTrue() {
10215 return true;
10216} // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10217
10218
10219Event.prototype = {
10220 instanceString: function instanceString() {
10221 return 'event';
10222 },
10223 recycle: function recycle(src, props) {
10224 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10225
10226 if (src != null && src.preventDefault) {
10227 // Browser Event object
10228 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10229 // by a handler lower down the tree; reflect the correct value.
10230
10231 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10232 } else if (src != null && src.type) {
10233 // Plain object containing all event details
10234 props = src;
10235 } else {
10236 // Event string
10237 this.type = src;
10238 } // Put explicitly provided properties onto the event object
10239
10240
10241 if (props != null) {
10242 // more efficient to manually copy fields we use
10243 this.originalEvent = props.originalEvent;
10244 this.type = props.type != null ? props.type : this.type;
10245 this.cy = props.cy;
10246 this.target = props.target;
10247 this.position = props.position;
10248 this.renderedPosition = props.renderedPosition;
10249 this.namespace = props.namespace;
10250 this.layout = props.layout;
10251 }
10252
10253 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10254 // create a rendered position based on the passed position
10255 var pos = this.position;
10256 var zoom = this.cy.zoom();
10257 var pan = this.cy.pan();
10258 this.renderedPosition = {
10259 x: pos.x * zoom + pan.x,
10260 y: pos.y * zoom + pan.y
10261 };
10262 } // Create a timestamp if incoming event doesn't have one
10263
10264
10265 this.timeStamp = src && src.timeStamp || Date.now();
10266 },
10267 preventDefault: function preventDefault() {
10268 this.isDefaultPrevented = returnTrue;
10269 var e = this.originalEvent;
10270
10271 if (!e) {
10272 return;
10273 } // if preventDefault exists run it on the original event
10274
10275
10276 if (e.preventDefault) {
10277 e.preventDefault();
10278 }
10279 },
10280 stopPropagation: function stopPropagation() {
10281 this.isPropagationStopped = returnTrue;
10282 var e = this.originalEvent;
10283
10284 if (!e) {
10285 return;
10286 } // if stopPropagation exists run it on the original event
10287
10288
10289 if (e.stopPropagation) {
10290 e.stopPropagation();
10291 }
10292 },
10293 stopImmediatePropagation: function stopImmediatePropagation() {
10294 this.isImmediatePropagationStopped = returnTrue;
10295 this.stopPropagation();
10296 },
10297 isDefaultPrevented: returnFalse,
10298 isPropagationStopped: returnFalse,
10299 isImmediatePropagationStopped: returnFalse
10300};
10301
10302var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
10303
10304var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
10305
10306var defaults$8 = {
10307 qualifierCompare: function qualifierCompare(q1, q2) {
10308 return q1 === q2;
10309 },
10310 eventMatches: function eventMatches()
10311 /*context, listener, eventObj*/
10312 {
10313 return true;
10314 },
10315 addEventFields: function addEventFields()
10316 /*context, evt*/
10317 {},
10318 callbackContext: function callbackContext(context
10319 /*, listener, eventObj*/
10320 ) {
10321 return context;
10322 },
10323 beforeEmit: function beforeEmit()
10324 /* context, listener, eventObj */
10325 {},
10326 afterEmit: function afterEmit()
10327 /* context, listener, eventObj */
10328 {},
10329 bubble: function bubble()
10330 /*context*/
10331 {
10332 return false;
10333 },
10334 parent: function parent()
10335 /*context*/
10336 {
10337 return null;
10338 },
10339 context: null
10340};
10341var defaultsKeys = Object.keys(defaults$8);
10342var emptyOpts = {};
10343
10344function Emitter() {
10345 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
10346 var context = arguments.length > 1 ? arguments[1] : undefined;
10347
10348 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
10349 for (var i = 0; i < defaultsKeys.length; i++) {
10350 var key = defaultsKeys[i];
10351 this[key] = opts[key] || defaults$8[key];
10352 }
10353
10354 this.context = context || this.context;
10355 this.listeners = [];
10356 this.emitting = 0;
10357}
10358
10359var p = Emitter.prototype;
10360
10361var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
10362 if (fn(qualifier)) {
10363 callback = qualifier;
10364 qualifier = null;
10365 }
10366
10367 if (confOverrides) {
10368 if (conf == null) {
10369 conf = confOverrides;
10370 } else {
10371 conf = extend({}, conf, confOverrides);
10372 }
10373 }
10374
10375 var eventList = array(events) ? events : events.split(/\s+/);
10376
10377 for (var i = 0; i < eventList.length; i++) {
10378 var evt = eventList[i];
10379
10380 if (emptyString(evt)) {
10381 continue;
10382 }
10383
10384 var match = evt.match(eventRegex); // type[.namespace]
10385
10386 if (match) {
10387 var type = match[1];
10388 var namespace = match[2] ? match[2] : null;
10389 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
10390
10391 if (ret === false) {
10392 break;
10393 } // allow exiting early
10394
10395 }
10396 }
10397};
10398
10399var makeEventObj = function makeEventObj(self, obj) {
10400 self.addEventFields(self.context, obj);
10401 return new Event(obj.type, obj);
10402};
10403
10404var forEachEventObj = function forEachEventObj(self, handler, events) {
10405 if (event(events)) {
10406 handler(self, events);
10407 return;
10408 } else if (plainObject(events)) {
10409 handler(self, makeEventObj(self, events));
10410 return;
10411 }
10412
10413 var eventList = array(events) ? events : events.split(/\s+/);
10414
10415 for (var i = 0; i < eventList.length; i++) {
10416 var evt = eventList[i];
10417
10418 if (emptyString(evt)) {
10419 continue;
10420 }
10421
10422 var match = evt.match(eventRegex); // type[.namespace]
10423
10424 if (match) {
10425 var type = match[1];
10426 var namespace = match[2] ? match[2] : null;
10427 var eventObj = makeEventObj(self, {
10428 type: type,
10429 namespace: namespace,
10430 target: self.context
10431 });
10432 handler(self, eventObj);
10433 }
10434 }
10435};
10436
10437p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
10438 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
10439 if (fn(callback)) {
10440 self.listeners.push({
10441 event: event,
10442 // full event string
10443 callback: callback,
10444 // callback to run
10445 type: type,
10446 // the event type (e.g. 'click')
10447 namespace: namespace,
10448 // the event namespace (e.g. ".foo")
10449 qualifier: qualifier,
10450 // a restriction on whether to match this emitter
10451 conf: conf // additional configuration
10452
10453 });
10454 }
10455 }, events, qualifier, callback, conf, confOverrides);
10456 return this;
10457};
10458
10459p.one = function (events, qualifier, callback, conf) {
10460 return this.on(events, qualifier, callback, conf, {
10461 one: true
10462 });
10463};
10464
10465p.removeListener = p.off = function (events, qualifier, callback, conf) {
10466 var _this = this;
10467
10468 if (this.emitting !== 0) {
10469 this.listeners = copyArray(this.listeners);
10470 }
10471
10472 var listeners = this.listeners;
10473
10474 var _loop = function _loop(i) {
10475 var listener = listeners[i];
10476 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
10477 /*, conf*/
10478 ) {
10479 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
10480 listeners.splice(i, 1);
10481 return false;
10482 }
10483 }, events, qualifier, callback, conf);
10484 };
10485
10486 for (var i = listeners.length - 1; i >= 0; i--) {
10487 _loop(i);
10488 }
10489
10490 return this;
10491};
10492
10493p.removeAllListeners = function () {
10494 return this.removeListener('*');
10495};
10496
10497p.emit = p.trigger = function (events, extraParams, manualCallback) {
10498 var listeners = this.listeners;
10499 var numListenersBeforeEmit = listeners.length;
10500 this.emitting++;
10501
10502 if (!array(extraParams)) {
10503 extraParams = [extraParams];
10504 }
10505
10506 forEachEventObj(this, function (self, eventObj) {
10507 if (manualCallback != null) {
10508 listeners = [{
10509 event: eventObj.event,
10510 type: eventObj.type,
10511 namespace: eventObj.namespace,
10512 callback: manualCallback
10513 }];
10514 numListenersBeforeEmit = listeners.length;
10515 }
10516
10517 var _loop2 = function _loop2(i) {
10518 var listener = listeners[i];
10519
10520 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
10521 var args = [eventObj];
10522
10523 if (extraParams != null) {
10524 push(args, extraParams);
10525 }
10526
10527 self.beforeEmit(self.context, listener, eventObj);
10528
10529 if (listener.conf && listener.conf.one) {
10530 self.listeners = self.listeners.filter(function (l) {
10531 return l !== listener;
10532 });
10533 }
10534
10535 var context = self.callbackContext(self.context, listener, eventObj);
10536 var ret = listener.callback.apply(context, args);
10537 self.afterEmit(self.context, listener, eventObj);
10538
10539 if (ret === false) {
10540 eventObj.stopPropagation();
10541 eventObj.preventDefault();
10542 }
10543 } // if listener matches
10544
10545 };
10546
10547 for (var i = 0; i < numListenersBeforeEmit; i++) {
10548 _loop2(i);
10549 } // for listener
10550
10551
10552 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
10553 self.parent(self.context).emit(eventObj, extraParams);
10554 }
10555 }, events);
10556 this.emitting--;
10557 return this;
10558};
10559
10560var emitterOptions = {
10561 qualifierCompare: function qualifierCompare(selector1, selector2) {
10562 if (selector1 == null || selector2 == null) {
10563 return selector1 == null && selector2 == null;
10564 } else {
10565 return selector1.sameText(selector2);
10566 }
10567 },
10568 eventMatches: function eventMatches(ele, listener, eventObj) {
10569 var selector = listener.qualifier;
10570
10571 if (selector != null) {
10572 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
10573 }
10574
10575 return true;
10576 },
10577 addEventFields: function addEventFields(ele, evt) {
10578 evt.cy = ele.cy();
10579 evt.target = ele;
10580 },
10581 callbackContext: function callbackContext(ele, listener, eventObj) {
10582 return listener.qualifier != null ? eventObj.target : ele;
10583 },
10584 beforeEmit: function beforeEmit(context, listener
10585 /*, eventObj*/
10586 ) {
10587 if (listener.conf && listener.conf.once) {
10588 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
10589 }
10590 },
10591 bubble: function bubble() {
10592 return true;
10593 },
10594 parent: function parent(ele) {
10595 return ele.isChild() ? ele.parent() : ele.cy();
10596 }
10597};
10598
10599var argSelector = function argSelector(arg) {
10600 if (string(arg)) {
10601 return new Selector(arg);
10602 } else {
10603 return arg;
10604 }
10605};
10606
10607var elesfn$m = {
10608 createEmitter: function createEmitter() {
10609 for (var i = 0; i < this.length; i++) {
10610 var ele = this[i];
10611 var _p = ele._private;
10612
10613 if (!_p.emitter) {
10614 _p.emitter = new Emitter(emitterOptions, ele);
10615 }
10616 }
10617
10618 return this;
10619 },
10620 emitter: function emitter() {
10621 return this._private.emitter;
10622 },
10623 on: function on(events, selector, callback) {
10624 var argSel = argSelector(selector);
10625
10626 for (var i = 0; i < this.length; i++) {
10627 var ele = this[i];
10628 ele.emitter().on(events, argSel, callback);
10629 }
10630
10631 return this;
10632 },
10633 removeListener: function removeListener(events, selector, callback) {
10634 var argSel = argSelector(selector);
10635
10636 for (var i = 0; i < this.length; i++) {
10637 var ele = this[i];
10638 ele.emitter().removeListener(events, argSel, callback);
10639 }
10640
10641 return this;
10642 },
10643 removeAllListeners: function removeAllListeners() {
10644 for (var i = 0; i < this.length; i++) {
10645 var ele = this[i];
10646 ele.emitter().removeAllListeners();
10647 }
10648
10649 return this;
10650 },
10651 one: function one(events, selector, callback) {
10652 var argSel = argSelector(selector);
10653
10654 for (var i = 0; i < this.length; i++) {
10655 var ele = this[i];
10656 ele.emitter().one(events, argSel, callback);
10657 }
10658
10659 return this;
10660 },
10661 once: function once(events, selector, callback) {
10662 var argSel = argSelector(selector);
10663
10664 for (var i = 0; i < this.length; i++) {
10665 var ele = this[i];
10666 ele.emitter().on(events, argSel, callback, {
10667 once: true,
10668 onceCollection: this
10669 });
10670 }
10671 },
10672 emit: function emit(events, extraParams) {
10673 for (var i = 0; i < this.length; i++) {
10674 var ele = this[i];
10675 ele.emitter().emit(events, extraParams);
10676 }
10677
10678 return this;
10679 },
10680 emitAndNotify: function emitAndNotify(event, extraParams) {
10681 // for internal use only
10682 if (this.length === 0) {
10683 return;
10684 } // empty collections don't need to notify anything
10685 // notify renderer
10686
10687
10688 this.cy().notify(event, this);
10689 this.emit(event, extraParams);
10690 return this;
10691 }
10692};
10693define$3.eventAliasesOn(elesfn$m);
10694
10695var elesfn$n = {
10696 nodes: function nodes(selector) {
10697 return this.filter(function (ele) {
10698 return ele.isNode();
10699 }).filter(selector);
10700 },
10701 edges: function edges(selector) {
10702 return this.filter(function (ele) {
10703 return ele.isEdge();
10704 }).filter(selector);
10705 },
10706 // internal helper to get nodes and edges as separate collections with single iteration over elements
10707 byGroup: function byGroup() {
10708 var nodes = this.spawn();
10709 var edges = this.spawn();
10710
10711 for (var i = 0; i < this.length; i++) {
10712 var ele = this[i];
10713
10714 if (ele.isNode()) {
10715 nodes.merge(ele);
10716 } else {
10717 edges.merge(ele);
10718 }
10719 }
10720
10721 return {
10722 nodes: nodes,
10723 edges: edges
10724 };
10725 },
10726 filter: function filter(_filter, thisArg) {
10727 if (_filter === undefined) {
10728 // check this first b/c it's the most common/performant case
10729 return this;
10730 } else if (string(_filter) || elementOrCollection(_filter)) {
10731 return new Selector(_filter).filter(this);
10732 } else if (fn(_filter)) {
10733 var filterEles = this.spawn();
10734 var eles = this;
10735
10736 for (var i = 0; i < eles.length; i++) {
10737 var ele = eles[i];
10738 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
10739
10740 if (include) {
10741 filterEles.merge(ele);
10742 }
10743 }
10744
10745 return filterEles;
10746 }
10747
10748 return this.spawn(); // if not handled by above, give 'em an empty collection
10749 },
10750 not: function not(toRemove) {
10751 if (!toRemove) {
10752 return this;
10753 } else {
10754 if (string(toRemove)) {
10755 toRemove = this.filter(toRemove);
10756 }
10757
10758 var elements = [];
10759 var rMap = toRemove._private.map;
10760
10761 for (var i = 0; i < this.length; i++) {
10762 var element = this[i];
10763 var remove = rMap.has(element.id());
10764
10765 if (!remove) {
10766 elements.push(element);
10767 }
10768 }
10769
10770 return this.spawn(elements);
10771 }
10772 },
10773 absoluteComplement: function absoluteComplement() {
10774 var cy = this.cy();
10775 return cy.mutableElements().not(this);
10776 },
10777 intersect: function intersect(other) {
10778 // if a selector is specified, then filter by it instead
10779 if (string(other)) {
10780 var selector = other;
10781 return this.filter(selector);
10782 }
10783
10784 var elements = [];
10785 var col1 = this;
10786 var col2 = other;
10787 var col1Smaller = this.length < other.length;
10788 var map2 = col1Smaller ? col2._private.map : col1._private.map;
10789 var col = col1Smaller ? col1 : col2;
10790
10791 for (var i = 0; i < col.length; i++) {
10792 var id = col[i]._private.data.id;
10793 var entry = map2.get(id);
10794
10795 if (entry) {
10796 elements.push(entry.ele);
10797 }
10798 }
10799
10800 return this.spawn(elements);
10801 },
10802 xor: function xor(other) {
10803 var cy = this._private.cy;
10804
10805 if (string(other)) {
10806 other = cy.$(other);
10807 }
10808
10809 var elements = [];
10810 var col1 = this;
10811 var col2 = other;
10812
10813 var add = function add(col, other) {
10814 for (var i = 0; i < col.length; i++) {
10815 var ele = col[i];
10816 var id = ele._private.data.id;
10817 var inOther = other.hasElementWithId(id);
10818
10819 if (!inOther) {
10820 elements.push(ele);
10821 }
10822 }
10823 };
10824
10825 add(col1, col2);
10826 add(col2, col1);
10827 return this.spawn(elements);
10828 },
10829 diff: function diff(other) {
10830 var cy = this._private.cy;
10831
10832 if (string(other)) {
10833 other = cy.$(other);
10834 }
10835
10836 var left = [];
10837 var right = [];
10838 var both = [];
10839 var col1 = this;
10840 var col2 = other;
10841
10842 var add = function add(col, other, retEles) {
10843 for (var i = 0; i < col.length; i++) {
10844 var ele = col[i];
10845 var id = ele._private.data.id;
10846 var inOther = other.hasElementWithId(id);
10847
10848 if (inOther) {
10849 both.push(ele);
10850 } else {
10851 retEles.push(ele);
10852 }
10853 }
10854 };
10855
10856 add(col1, col2, left);
10857 add(col2, col1, right);
10858 return {
10859 left: this.spawn(left, {
10860 unique: true
10861 }),
10862 right: this.spawn(right, {
10863 unique: true
10864 }),
10865 both: this.spawn(both, {
10866 unique: true
10867 })
10868 };
10869 },
10870 add: function add(toAdd) {
10871 var cy = this._private.cy;
10872
10873 if (!toAdd) {
10874 return this;
10875 }
10876
10877 if (string(toAdd)) {
10878 var selector = toAdd;
10879 toAdd = cy.mutableElements().filter(selector);
10880 }
10881
10882 var elements = [];
10883
10884 for (var i = 0; i < this.length; i++) {
10885 elements.push(this[i]);
10886 }
10887
10888 var map = this._private.map;
10889
10890 for (var _i = 0; _i < toAdd.length; _i++) {
10891 var add = !map.has(toAdd[_i].id());
10892
10893 if (add) {
10894 elements.push(toAdd[_i]);
10895 }
10896 }
10897
10898 return this.spawn(elements);
10899 },
10900 // in place merge on calling collection
10901 merge: function merge(toAdd) {
10902 var _p = this._private;
10903 var cy = _p.cy;
10904
10905 if (!toAdd) {
10906 return this;
10907 }
10908
10909 if (toAdd && string(toAdd)) {
10910 var selector = toAdd;
10911 toAdd = cy.mutableElements().filter(selector);
10912 }
10913
10914 var map = _p.map;
10915
10916 for (var i = 0; i < toAdd.length; i++) {
10917 var toAddEle = toAdd[i];
10918 var id = toAddEle._private.data.id;
10919 var add = !map.has(id);
10920
10921 if (add) {
10922 var index = this.length++;
10923 this[index] = toAddEle;
10924 map.set(id, {
10925 ele: toAddEle,
10926 index: index
10927 });
10928 } else {
10929 // replace
10930 var _index = map.get(id).index;
10931 this[_index] = toAddEle;
10932 map.set(id, {
10933 ele: toAddEle,
10934 index: _index
10935 });
10936 }
10937 }
10938
10939 return this; // chaining
10940 },
10941 unmergeAt: function unmergeAt(i) {
10942 var ele = this[i];
10943 var id = ele.id();
10944 var _p = this._private;
10945 var map = _p.map; // remove ele
10946
10947 this[i] = undefined;
10948 map["delete"](id);
10949 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
10950
10951 if (this.length > 1 && !unmergedLastEle) {
10952 var lastEleI = this.length - 1;
10953 var lastEle = this[lastEleI];
10954 var lastEleId = lastEle._private.data.id;
10955 this[lastEleI] = undefined;
10956 this[i] = lastEle;
10957 map.set(lastEleId, {
10958 ele: lastEle,
10959 index: i
10960 });
10961 } // the collection is now 1 ele smaller
10962
10963
10964 this.length--;
10965 return this;
10966 },
10967 // remove single ele in place in calling collection
10968 unmergeOne: function unmergeOne(ele) {
10969 ele = ele[0];
10970 var _p = this._private;
10971 var id = ele._private.data.id;
10972 var map = _p.map;
10973 var entry = map.get(id);
10974
10975 if (!entry) {
10976 return this; // no need to remove
10977 }
10978
10979 var i = entry.index;
10980 this.unmergeAt(i);
10981 return this;
10982 },
10983 // remove eles in place on calling collection
10984 unmerge: function unmerge(toRemove) {
10985 var cy = this._private.cy;
10986
10987 if (!toRemove) {
10988 return this;
10989 }
10990
10991 if (toRemove && string(toRemove)) {
10992 var selector = toRemove;
10993 toRemove = cy.mutableElements().filter(selector);
10994 }
10995
10996 for (var i = 0; i < toRemove.length; i++) {
10997 this.unmergeOne(toRemove[i]);
10998 }
10999
11000 return this; // chaining
11001 },
11002 unmergeBy: function unmergeBy(toRmFn) {
11003 for (var i = this.length - 1; i >= 0; i--) {
11004 var ele = this[i];
11005
11006 if (toRmFn(ele)) {
11007 this.unmergeAt(i);
11008 }
11009 }
11010
11011 return this;
11012 },
11013 map: function map(mapFn, thisArg) {
11014 var arr = [];
11015 var eles = this;
11016
11017 for (var i = 0; i < eles.length; i++) {
11018 var ele = eles[i];
11019 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
11020 arr.push(ret);
11021 }
11022
11023 return arr;
11024 },
11025 reduce: function reduce(fn, initialValue) {
11026 var val = initialValue;
11027 var eles = this;
11028
11029 for (var i = 0; i < eles.length; i++) {
11030 val = fn(val, eles[i], i, eles);
11031 }
11032
11033 return val;
11034 },
11035 max: function max(valFn, thisArg) {
11036 var max = -Infinity;
11037 var maxEle;
11038 var eles = this;
11039
11040 for (var i = 0; i < eles.length; i++) {
11041 var ele = eles[i];
11042 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11043
11044 if (val > max) {
11045 max = val;
11046 maxEle = ele;
11047 }
11048 }
11049
11050 return {
11051 value: max,
11052 ele: maxEle
11053 };
11054 },
11055 min: function min(valFn, thisArg) {
11056 var min = Infinity;
11057 var minEle;
11058 var eles = this;
11059
11060 for (var i = 0; i < eles.length; i++) {
11061 var ele = eles[i];
11062 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11063
11064 if (val < min) {
11065 min = val;
11066 minEle = ele;
11067 }
11068 }
11069
11070 return {
11071 value: min,
11072 ele: minEle
11073 };
11074 }
11075}; // aliases
11076
11077var fn$5 = elesfn$n;
11078fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
11079fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
11080fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
11081fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
11082fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
11083fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
11084
11085var elesfn$o = {
11086 isNode: function isNode() {
11087 return this.group() === 'nodes';
11088 },
11089 isEdge: function isEdge() {
11090 return this.group() === 'edges';
11091 },
11092 isLoop: function isLoop() {
11093 return this.isEdge() && this.source()[0] === this.target()[0];
11094 },
11095 isSimple: function isSimple() {
11096 return this.isEdge() && this.source()[0] !== this.target()[0];
11097 },
11098 group: function group() {
11099 var ele = this[0];
11100
11101 if (ele) {
11102 return ele._private.group;
11103 }
11104 }
11105};
11106
11107/**
11108 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11109 * and z-index (low to high). These styles affect how this applies:
11110 *
11111 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11112 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11113 * root to leaves of the compound graph. The last drawn is `top`.
11114 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11115 * `manual` ignores this convention and draws based on the `z-index` value setting.
11116 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11117 * `z-index` will be drawn on top of an element with a lower `z-index`.
11118 */
11119
11120var zIndexSort = function zIndexSort(a, b) {
11121 var cy = a.cy();
11122 var hasCompoundNodes = cy.hasCompoundNodes();
11123
11124 function getDepth(ele) {
11125 var style = ele.pstyle('z-compound-depth');
11126
11127 if (style.value === 'auto') {
11128 return hasCompoundNodes ? ele.zDepth() : 0;
11129 } else if (style.value === 'bottom') {
11130 return -1;
11131 } else if (style.value === 'top') {
11132 return MAX_INT;
11133 } // 'orphan'
11134
11135
11136 return 0;
11137 }
11138
11139 var depthDiff = getDepth(a) - getDepth(b);
11140
11141 if (depthDiff !== 0) {
11142 return depthDiff;
11143 }
11144
11145 function getEleDepth(ele) {
11146 var style = ele.pstyle('z-index-compare');
11147
11148 if (style.value === 'auto') {
11149 return ele.isNode() ? 1 : 0;
11150 } // 'manual'
11151
11152
11153 return 0;
11154 }
11155
11156 var eleDiff = getEleDepth(a) - getEleDepth(b);
11157
11158 if (eleDiff !== 0) {
11159 return eleDiff;
11160 }
11161
11162 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11163
11164 if (zDiff !== 0) {
11165 return zDiff;
11166 } // compare indices in the core (order added to graph w/ last on top)
11167
11168
11169 return a.poolIndex() - b.poolIndex();
11170};
11171
11172var elesfn$p = {
11173 forEach: function forEach(fn$1, thisArg) {
11174 if (fn(fn$1)) {
11175 var N = this.length;
11176
11177 for (var i = 0; i < N; i++) {
11178 var ele = this[i];
11179 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
11180
11181 if (ret === false) {
11182 break;
11183 } // exit each early on return false
11184
11185 }
11186 }
11187
11188 return this;
11189 },
11190 toArray: function toArray() {
11191 var array = [];
11192
11193 for (var i = 0; i < this.length; i++) {
11194 array.push(this[i]);
11195 }
11196
11197 return array;
11198 },
11199 slice: function slice(start, end) {
11200 var array = [];
11201 var thisSize = this.length;
11202
11203 if (end == null) {
11204 end = thisSize;
11205 }
11206
11207 if (start == null) {
11208 start = 0;
11209 }
11210
11211 if (start < 0) {
11212 start = thisSize + start;
11213 }
11214
11215 if (end < 0) {
11216 end = thisSize + end;
11217 }
11218
11219 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11220 array.push(this[i]);
11221 }
11222
11223 return this.spawn(array);
11224 },
11225 size: function size() {
11226 return this.length;
11227 },
11228 eq: function eq(i) {
11229 return this[i] || this.spawn();
11230 },
11231 first: function first() {
11232 return this[0] || this.spawn();
11233 },
11234 last: function last() {
11235 return this[this.length - 1] || this.spawn();
11236 },
11237 empty: function empty() {
11238 return this.length === 0;
11239 },
11240 nonempty: function nonempty() {
11241 return !this.empty();
11242 },
11243 sort: function sort(sortFn) {
11244 if (!fn(sortFn)) {
11245 return this;
11246 }
11247
11248 var sorted = this.toArray().sort(sortFn);
11249 return this.spawn(sorted);
11250 },
11251 sortByZIndex: function sortByZIndex() {
11252 return this.sort(zIndexSort);
11253 },
11254 zDepth: function zDepth() {
11255 var ele = this[0];
11256
11257 if (!ele) {
11258 return undefined;
11259 } // let cy = ele.cy();
11260
11261
11262 var _p = ele._private;
11263 var group = _p.group;
11264
11265 if (group === 'nodes') {
11266 var depth = _p.data.parent ? ele.parents().size() : 0;
11267
11268 if (!ele.isParent()) {
11269 return MAX_INT - 1; // childless nodes always on top
11270 }
11271
11272 return depth;
11273 } else {
11274 var src = _p.source;
11275 var tgt = _p.target;
11276 var srcDepth = src.zDepth();
11277 var tgtDepth = tgt.zDepth();
11278 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11279 }
11280 }
11281};
11282elesfn$p.each = elesfn$p.forEach;
11283
11284var defineSymbolIterator = function defineSymbolIterator() {
11285 var typeofUndef = "undefined" ;
11286 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
11287
11288 if (isIteratorSupported) {
11289 elesfn$p[Symbol.iterator] = function () {
11290 var _this = this;
11291
11292 // eslint-disable-line no-undef
11293 var entry = {
11294 value: undefined,
11295 done: false
11296 };
11297 var i = 0;
11298 var length = this.length;
11299 return _defineProperty({
11300 next: function next() {
11301 if (i < length) {
11302 entry.value = _this[i++];
11303 } else {
11304 entry.value = undefined;
11305 entry.done = true;
11306 }
11307
11308 return entry;
11309 }
11310 }, Symbol.iterator, function () {
11311 // eslint-disable-line no-undef
11312 return this;
11313 });
11314 };
11315 }
11316};
11317
11318defineSymbolIterator();
11319
11320var getLayoutDimensionOptions = defaults({
11321 nodeDimensionsIncludeLabels: false
11322});
11323var elesfn$q = {
11324 // Calculates and returns node dimensions { x, y } based on options given
11325 layoutDimensions: function layoutDimensions(options) {
11326 options = getLayoutDimensionOptions(options);
11327 var dims;
11328
11329 if (!this.takesUpSpace()) {
11330 dims = {
11331 w: 0,
11332 h: 0
11333 };
11334 } else if (options.nodeDimensionsIncludeLabels) {
11335 var bbDim = this.boundingBox();
11336 dims = {
11337 w: bbDim.w,
11338 h: bbDim.h
11339 };
11340 } else {
11341 dims = {
11342 w: this.outerWidth(),
11343 h: this.outerHeight()
11344 };
11345 } // sanitise the dimensions for external layouts (avoid division by zero)
11346
11347
11348 if (dims.w === 0 || dims.h === 0) {
11349 dims.w = dims.h = 1;
11350 }
11351
11352 return dims;
11353 },
11354 // using standard layout options, apply position function (w/ or w/o animation)
11355 layoutPositions: function layoutPositions(layout, options, fn) {
11356 var nodes = this.nodes();
11357 var cy = this.cy();
11358 var layoutEles = options.eles; // nodes & edges
11359
11360 var getMemoizeKey = function getMemoizeKey(node) {
11361 return node.id();
11362 };
11363
11364 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
11365
11366 layout.emit({
11367 type: 'layoutstart',
11368 layout: layout
11369 });
11370 layout.animations = [];
11371
11372 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
11373 var center = {
11374 x: nodesBb.x1 + nodesBb.w / 2,
11375 y: nodesBb.y1 + nodesBb.h / 2
11376 };
11377 var spacingVector = {
11378 // scale from center of bounding box (not necessarily 0,0)
11379 x: (pos.x - center.x) * spacing,
11380 y: (pos.y - center.y) * spacing
11381 };
11382 return {
11383 x: center.x + spacingVector.x,
11384 y: center.y + spacingVector.y
11385 };
11386 };
11387
11388 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
11389
11390 var spacingBb = function spacingBb() {
11391 if (!useSpacingFactor) {
11392 return null;
11393 }
11394
11395 var bb = makeBoundingBox();
11396
11397 for (var i = 0; i < nodes.length; i++) {
11398 var node = nodes[i];
11399 var pos = fnMem(node, i);
11400 expandBoundingBoxByPoint(bb, pos.x, pos.y);
11401 }
11402
11403 return bb;
11404 };
11405
11406 var bb = spacingBb();
11407 var getFinalPos = memoize(function (node, i) {
11408 var newPos = fnMem(node, i);
11409
11410 if (useSpacingFactor) {
11411 var spacing = Math.abs(options.spacingFactor);
11412 newPos = calculateSpacing(spacing, bb, newPos);
11413 }
11414
11415 if (options.transform != null) {
11416 newPos = options.transform(node, newPos);
11417 }
11418
11419 return newPos;
11420 }, getMemoizeKey);
11421
11422 if (options.animate) {
11423 for (var i = 0; i < nodes.length; i++) {
11424 var node = nodes[i];
11425 var newPos = getFinalPos(node, i);
11426 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
11427
11428 if (animateNode) {
11429 var ani = node.animation({
11430 position: newPos,
11431 duration: options.animationDuration,
11432 easing: options.animationEasing
11433 });
11434 layout.animations.push(ani);
11435 } else {
11436 node.position(newPos);
11437 }
11438 }
11439
11440 if (options.fit) {
11441 var fitAni = cy.animation({
11442 fit: {
11443 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
11444 padding: options.padding
11445 },
11446 duration: options.animationDuration,
11447 easing: options.animationEasing
11448 });
11449 layout.animations.push(fitAni);
11450 } else if (options.zoom !== undefined && options.pan !== undefined) {
11451 var zoomPanAni = cy.animation({
11452 zoom: options.zoom,
11453 pan: options.pan,
11454 duration: options.animationDuration,
11455 easing: options.animationEasing
11456 });
11457 layout.animations.push(zoomPanAni);
11458 }
11459
11460 layout.animations.forEach(function (ani) {
11461 return ani.play();
11462 });
11463 layout.one('layoutready', options.ready);
11464 layout.emit({
11465 type: 'layoutready',
11466 layout: layout
11467 });
11468 Promise$1.all(layout.animations.map(function (ani) {
11469 return ani.promise();
11470 })).then(function () {
11471 layout.one('layoutstop', options.stop);
11472 layout.emit({
11473 type: 'layoutstop',
11474 layout: layout
11475 });
11476 });
11477 } else {
11478 nodes.positions(getFinalPos);
11479
11480 if (options.fit) {
11481 cy.fit(options.eles, options.padding);
11482 }
11483
11484 if (options.zoom != null) {
11485 cy.zoom(options.zoom);
11486 }
11487
11488 if (options.pan) {
11489 cy.pan(options.pan);
11490 }
11491
11492 layout.one('layoutready', options.ready);
11493 layout.emit({
11494 type: 'layoutready',
11495 layout: layout
11496 });
11497 layout.one('layoutstop', options.stop);
11498 layout.emit({
11499 type: 'layoutstop',
11500 layout: layout
11501 });
11502 }
11503
11504 return this; // chaining
11505 },
11506 layout: function layout(options) {
11507 var cy = this.cy();
11508 return cy.makeLayout(extend({}, options, {
11509 eles: this
11510 }));
11511 }
11512}; // aliases:
11513
11514elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
11515
11516function styleCache(key, fn, ele) {
11517 var _p = ele._private;
11518 var cache = _p.styleCache = _p.styleCache || [];
11519 var val;
11520
11521 if ((val = cache[key]) != null) {
11522 return val;
11523 } else {
11524 val = cache[key] = fn(ele);
11525 return val;
11526 }
11527}
11528
11529function cacheStyleFunction(key, fn) {
11530 key = hashString(key);
11531 return function cachedStyleFunction(ele) {
11532 return styleCache(key, fn, ele);
11533 };
11534}
11535
11536function cachePrototypeStyleFunction(key, fn) {
11537 key = hashString(key);
11538
11539 var selfFn = function selfFn(ele) {
11540 return fn.call(ele);
11541 };
11542
11543 return function cachedPrototypeStyleFunction() {
11544 var ele = this[0];
11545
11546 if (ele) {
11547 return styleCache(key, selfFn, ele);
11548 }
11549 };
11550}
11551
11552var elesfn$r = {
11553 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
11554 var cy = this.cy();
11555 var renderer = cy.renderer();
11556 var styleEnabled = cy.styleEnabled();
11557
11558 if (renderer && styleEnabled) {
11559 renderer.recalculateRenderedStyle(this, useCache);
11560 }
11561
11562 return this;
11563 },
11564 dirtyStyleCache: function dirtyStyleCache() {
11565 var cy = this.cy();
11566
11567 var dirty = function dirty(ele) {
11568 return ele._private.styleCache = null;
11569 };
11570
11571 if (cy.hasCompoundNodes()) {
11572 var eles;
11573 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11574 eles.merge(eles.connectedEdges());
11575 eles.forEach(dirty);
11576 } else {
11577 this.forEach(function (ele) {
11578 dirty(ele);
11579 ele.connectedEdges().forEach(dirty);
11580 });
11581 }
11582
11583 return this;
11584 },
11585 // fully updates (recalculates) the style for the elements
11586 updateStyle: function updateStyle(notifyRenderer) {
11587 var cy = this._private.cy;
11588
11589 if (!cy.styleEnabled()) {
11590 return this;
11591 }
11592
11593 if (cy.batching()) {
11594 var bEles = cy._private.batchStyleEles;
11595 bEles.merge(this);
11596 return this; // chaining and exit early when batching
11597 }
11598
11599 var hasCompounds = cy.hasCompoundNodes();
11600 var style = cy.style();
11601 var updatedEles = this;
11602 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
11603
11604 if (hasCompounds) {
11605 // then add everything up and down for compound selector checks
11606 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11607 }
11608
11609 var changedEles = style.apply(updatedEles);
11610
11611 if (notifyRenderer) {
11612 changedEles.emitAndNotify('style'); // let renderer know we changed style
11613 } else {
11614 changedEles.emit('style'); // just fire the event
11615 }
11616
11617 return this; // chaining
11618 },
11619 // get the internal parsed style object for the specified property
11620 parsedStyle: function parsedStyle(property) {
11621 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11622 var ele = this[0];
11623 var cy = ele.cy();
11624
11625 if (!cy.styleEnabled()) {
11626 return;
11627 }
11628
11629 if (ele) {
11630 var overriddenStyle = ele._private.style[property];
11631
11632 if (overriddenStyle != null) {
11633 return overriddenStyle;
11634 } else if (includeNonDefault) {
11635 return cy.style().getDefaultProperty(property);
11636 } else {
11637 return null;
11638 }
11639 }
11640 },
11641 numericStyle: function numericStyle(property) {
11642 var ele = this[0];
11643
11644 if (!ele.cy().styleEnabled()) {
11645 return;
11646 }
11647
11648 if (ele) {
11649 var pstyle = ele.pstyle(property);
11650 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
11651 }
11652 },
11653 numericStyleUnits: function numericStyleUnits(property) {
11654 var ele = this[0];
11655
11656 if (!ele.cy().styleEnabled()) {
11657 return;
11658 }
11659
11660 if (ele) {
11661 return ele.pstyle(property).units;
11662 }
11663 },
11664 // get the specified css property as a rendered value (i.e. on-screen value)
11665 // or get the whole rendered style if no property specified (NB doesn't allow setting)
11666 renderedStyle: function renderedStyle(property) {
11667 var cy = this.cy();
11668
11669 if (!cy.styleEnabled()) {
11670 return this;
11671 }
11672
11673 var ele = this[0];
11674
11675 if (ele) {
11676 return cy.style().getRenderedStyle(ele, property);
11677 }
11678 },
11679 // read the calculated css style of the element or override the style (via a bypass)
11680 style: function style(name, value) {
11681 var cy = this.cy();
11682
11683 if (!cy.styleEnabled()) {
11684 return this;
11685 }
11686
11687 var updateTransitions = false;
11688 var style = cy.style();
11689
11690 if (plainObject(name)) {
11691 // then extend the bypass
11692 var props = name;
11693 style.applyBypass(this, props, updateTransitions);
11694 this.emitAndNotify('style'); // let the renderer know we've updated style
11695 } else if (string(name)) {
11696 if (value === undefined) {
11697 // then get the property from the style
11698 var ele = this[0];
11699
11700 if (ele) {
11701 return style.getStylePropertyValue(ele, name);
11702 } else {
11703 // empty collection => can't get any value
11704 return;
11705 }
11706 } else {
11707 // then set the bypass with the property value
11708 style.applyBypass(this, name, value, updateTransitions);
11709 this.emitAndNotify('style'); // let the renderer know we've updated style
11710 }
11711 } else if (name === undefined) {
11712 var _ele = this[0];
11713
11714 if (_ele) {
11715 return style.getRawStyle(_ele);
11716 } else {
11717 // empty collection => can't get any value
11718 return;
11719 }
11720 }
11721
11722 return this; // chaining
11723 },
11724 removeStyle: function removeStyle(names) {
11725 var cy = this.cy();
11726
11727 if (!cy.styleEnabled()) {
11728 return this;
11729 }
11730
11731 var updateTransitions = false;
11732 var style = cy.style();
11733 var eles = this;
11734
11735 if (names === undefined) {
11736 for (var i = 0; i < eles.length; i++) {
11737 var ele = eles[i];
11738 style.removeAllBypasses(ele, updateTransitions);
11739 }
11740 } else {
11741 names = names.split(/\s+/);
11742
11743 for (var _i = 0; _i < eles.length; _i++) {
11744 var _ele2 = eles[_i];
11745 style.removeBypasses(_ele2, names, updateTransitions);
11746 }
11747 }
11748
11749 this.emitAndNotify('style'); // let the renderer know we've updated style
11750
11751 return this; // chaining
11752 },
11753 show: function show() {
11754 this.css('display', 'element');
11755 return this; // chaining
11756 },
11757 hide: function hide() {
11758 this.css('display', 'none');
11759 return this; // chaining
11760 },
11761 effectiveOpacity: function effectiveOpacity() {
11762 var cy = this.cy();
11763
11764 if (!cy.styleEnabled()) {
11765 return 1;
11766 }
11767
11768 var hasCompoundNodes = cy.hasCompoundNodes();
11769 var ele = this[0];
11770
11771 if (ele) {
11772 var _p = ele._private;
11773 var parentOpacity = ele.pstyle('opacity').value;
11774
11775 if (!hasCompoundNodes) {
11776 return parentOpacity;
11777 }
11778
11779 var parents = !_p.data.parent ? null : ele.parents();
11780
11781 if (parents) {
11782 for (var i = 0; i < parents.length; i++) {
11783 var parent = parents[i];
11784 var opacity = parent.pstyle('opacity').value;
11785 parentOpacity = opacity * parentOpacity;
11786 }
11787 }
11788
11789 return parentOpacity;
11790 }
11791 },
11792 transparent: function transparent() {
11793 var cy = this.cy();
11794
11795 if (!cy.styleEnabled()) {
11796 return false;
11797 }
11798
11799 var ele = this[0];
11800 var hasCompoundNodes = ele.cy().hasCompoundNodes();
11801
11802 if (ele) {
11803 if (!hasCompoundNodes) {
11804 return ele.pstyle('opacity').value === 0;
11805 } else {
11806 return ele.effectiveOpacity() === 0;
11807 }
11808 }
11809 },
11810 backgrounding: function backgrounding() {
11811 var cy = this.cy();
11812
11813 if (!cy.styleEnabled()) {
11814 return false;
11815 }
11816
11817 var ele = this[0];
11818 return ele._private.backgrounding ? true : false;
11819 }
11820};
11821
11822function checkCompound(ele, parentOk) {
11823 var _p = ele._private;
11824 var parents = _p.data.parent ? ele.parents() : null;
11825
11826 if (parents) {
11827 for (var i = 0; i < parents.length; i++) {
11828 var parent = parents[i];
11829
11830 if (!parentOk(parent)) {
11831 return false;
11832 }
11833 }
11834 }
11835
11836 return true;
11837}
11838
11839function defineDerivedStateFunction(specs) {
11840 var ok = specs.ok;
11841 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
11842 var parentOk = specs.parentOk || specs.ok;
11843 return function () {
11844 var cy = this.cy();
11845
11846 if (!cy.styleEnabled()) {
11847 return true;
11848 }
11849
11850 var ele = this[0];
11851 var hasCompoundNodes = cy.hasCompoundNodes();
11852
11853 if (ele) {
11854 var _p = ele._private;
11855
11856 if (!ok(ele)) {
11857 return false;
11858 }
11859
11860 if (ele.isNode()) {
11861 return !hasCompoundNodes || checkCompound(ele, parentOk);
11862 } else {
11863 var src = _p.source;
11864 var tgt = _p.target;
11865 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
11866 }
11867 }
11868 };
11869}
11870
11871var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
11872 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
11873});
11874elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
11875 ok: eleTakesUpSpace
11876}));
11877var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
11878 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
11879});
11880var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
11881 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
11882});
11883elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
11884 ok: eleInteractive,
11885 parentOk: parentInteractive,
11886 edgeOkViaNode: eleTakesUpSpace
11887}));
11888
11889elesfn$r.noninteractive = function () {
11890 var ele = this[0];
11891
11892 if (ele) {
11893 return !ele.interactive();
11894 }
11895};
11896
11897var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
11898 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
11899});
11900var edgeVisibleViaNode = eleTakesUpSpace;
11901elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
11902 ok: eleVisible,
11903 edgeOkViaNode: edgeVisibleViaNode
11904}));
11905
11906elesfn$r.hidden = function () {
11907 var ele = this[0];
11908
11909 if (ele) {
11910 return !ele.visible();
11911 }
11912};
11913
11914elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
11915 if (!this.cy().styleEnabled()) {
11916 return false;
11917 }
11918
11919 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
11920});
11921elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
11922elesfn$r.renderedCss = elesfn$r.renderedStyle;
11923elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
11924elesfn$r.pstyle = elesfn$r.parsedStyle;
11925
11926var elesfn$s = {};
11927
11928function defineSwitchFunction(params) {
11929 return function () {
11930 var args = arguments;
11931 var changedEles = []; // e.g. cy.nodes().select( data, handler )
11932
11933 if (args.length === 2) {
11934 var data = args[0];
11935 var handler = args[1];
11936 this.on(params.event, data, handler);
11937 } // e.g. cy.nodes().select( handler )
11938 else if (args.length === 1 && fn(args[0])) {
11939 var _handler = args[0];
11940 this.on(params.event, _handler);
11941 } // e.g. cy.nodes().select()
11942 // e.g. (private) cy.nodes().select(['tapselect'])
11943 else if (args.length === 0 || args.length === 1 && array(args[0])) {
11944 var addlEvents = args.length === 1 ? args[0] : null;
11945
11946 for (var i = 0; i < this.length; i++) {
11947 var ele = this[i];
11948 var able = !params.ableField || ele._private[params.ableField];
11949 var changed = ele._private[params.field] != params.value;
11950
11951 if (params.overrideAble) {
11952 var overrideAble = params.overrideAble(ele);
11953
11954 if (overrideAble !== undefined) {
11955 able = overrideAble;
11956
11957 if (!overrideAble) {
11958 return this;
11959 } // to save cycles assume not able for all on override
11960
11961 }
11962 }
11963
11964 if (able) {
11965 ele._private[params.field] = params.value;
11966
11967 if (changed) {
11968 changedEles.push(ele);
11969 }
11970 }
11971 }
11972
11973 var changedColl = this.spawn(changedEles);
11974 changedColl.updateStyle(); // change of state => possible change of style
11975
11976 changedColl.emit(params.event);
11977
11978 if (addlEvents) {
11979 changedColl.emit(addlEvents);
11980 }
11981 }
11982
11983 return this;
11984 };
11985}
11986
11987function defineSwitchSet(params) {
11988 elesfn$s[params.field] = function () {
11989 var ele = this[0];
11990
11991 if (ele) {
11992 if (params.overrideField) {
11993 var val = params.overrideField(ele);
11994
11995 if (val !== undefined) {
11996 return val;
11997 }
11998 }
11999
12000 return ele._private[params.field];
12001 }
12002 };
12003
12004 elesfn$s[params.on] = defineSwitchFunction({
12005 event: params.on,
12006 field: params.field,
12007 ableField: params.ableField,
12008 overrideAble: params.overrideAble,
12009 value: true
12010 });
12011 elesfn$s[params.off] = defineSwitchFunction({
12012 event: params.off,
12013 field: params.field,
12014 ableField: params.ableField,
12015 overrideAble: params.overrideAble,
12016 value: false
12017 });
12018}
12019
12020defineSwitchSet({
12021 field: 'locked',
12022 overrideField: function overrideField(ele) {
12023 return ele.cy().autolock() ? true : undefined;
12024 },
12025 on: 'lock',
12026 off: 'unlock'
12027});
12028defineSwitchSet({
12029 field: 'grabbable',
12030 overrideField: function overrideField(ele) {
12031 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12032 },
12033 on: 'grabify',
12034 off: 'ungrabify'
12035});
12036defineSwitchSet({
12037 field: 'selected',
12038 ableField: 'selectable',
12039 overrideAble: function overrideAble(ele) {
12040 return ele.cy().autounselectify() ? false : undefined;
12041 },
12042 on: 'select',
12043 off: 'unselect'
12044});
12045defineSwitchSet({
12046 field: 'selectable',
12047 overrideField: function overrideField(ele) {
12048 return ele.cy().autounselectify() ? false : undefined;
12049 },
12050 on: 'selectify',
12051 off: 'unselectify'
12052});
12053elesfn$s.deselect = elesfn$s.unselect;
12054
12055elesfn$s.grabbed = function () {
12056 var ele = this[0];
12057
12058 if (ele) {
12059 return ele._private.grabbed;
12060 }
12061};
12062
12063defineSwitchSet({
12064 field: 'active',
12065 on: 'activate',
12066 off: 'unactivate'
12067});
12068defineSwitchSet({
12069 field: 'pannable',
12070 on: 'panify',
12071 off: 'unpanify'
12072});
12073
12074elesfn$s.inactive = function () {
12075 var ele = this[0];
12076
12077 if (ele) {
12078 return !ele._private.active;
12079 }
12080};
12081
12082var elesfn$t = {}; // DAG functions
12083////////////////
12084
12085var defineDagExtremity = function defineDagExtremity(params) {
12086 return function dagExtremityImpl(selector) {
12087 var eles = this;
12088 var ret = [];
12089
12090 for (var i = 0; i < eles.length; i++) {
12091 var ele = eles[i];
12092
12093 if (!ele.isNode()) {
12094 continue;
12095 }
12096
12097 var disqualified = false;
12098 var edges = ele.connectedEdges();
12099
12100 for (var j = 0; j < edges.length; j++) {
12101 var edge = edges[j];
12102 var src = edge.source();
12103 var tgt = edge.target();
12104
12105 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12106 disqualified = true;
12107 break;
12108 }
12109 }
12110
12111 if (!disqualified) {
12112 ret.push(ele);
12113 }
12114 }
12115
12116 return this.spawn(ret, {
12117 unique: true
12118 }).filter(selector);
12119 };
12120};
12121
12122var defineDagOneHop = function defineDagOneHop(params) {
12123 return function (selector) {
12124 var eles = this;
12125 var oEles = [];
12126
12127 for (var i = 0; i < eles.length; i++) {
12128 var ele = eles[i];
12129
12130 if (!ele.isNode()) {
12131 continue;
12132 }
12133
12134 var edges = ele.connectedEdges();
12135
12136 for (var j = 0; j < edges.length; j++) {
12137 var edge = edges[j];
12138 var src = edge.source();
12139 var tgt = edge.target();
12140
12141 if (params.outgoing && src === ele) {
12142 oEles.push(edge);
12143 oEles.push(tgt);
12144 } else if (params.incoming && tgt === ele) {
12145 oEles.push(edge);
12146 oEles.push(src);
12147 }
12148 }
12149 }
12150
12151 return this.spawn(oEles, {
12152 unique: true
12153 }).filter(selector);
12154 };
12155};
12156
12157var defineDagAllHops = function defineDagAllHops(params) {
12158 return function (selector) {
12159 var eles = this;
12160 var sEles = [];
12161 var sElesIds = {};
12162
12163 for (;;) {
12164 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12165
12166 if (next.length === 0) {
12167 break;
12168 } // done if none left
12169
12170
12171 var newNext = false;
12172
12173 for (var i = 0; i < next.length; i++) {
12174 var n = next[i];
12175 var nid = n.id();
12176
12177 if (!sElesIds[nid]) {
12178 sElesIds[nid] = true;
12179 sEles.push(n);
12180 newNext = true;
12181 }
12182 }
12183
12184 if (!newNext) {
12185 break;
12186 } // done if touched all outgoers already
12187
12188
12189 eles = next;
12190 }
12191
12192 return this.spawn(sEles, {
12193 unique: true
12194 }).filter(selector);
12195 };
12196};
12197
12198elesfn$t.clearTraversalCache = function () {
12199 for (var i = 0; i < this.length; i++) {
12200 this[i]._private.traversalCache = null;
12201 }
12202};
12203
12204extend(elesfn$t, {
12205 // get the root nodes in the DAG
12206 roots: defineDagExtremity({
12207 noIncomingEdges: true
12208 }),
12209 // get the leaf nodes in the DAG
12210 leaves: defineDagExtremity({
12211 noOutgoingEdges: true
12212 }),
12213 // normally called children in graph theory
12214 // these nodes =edges=> outgoing nodes
12215 outgoers: cache(defineDagOneHop({
12216 outgoing: true
12217 }), 'outgoers'),
12218 // aka DAG descendants
12219 successors: defineDagAllHops({
12220 outgoing: true
12221 }),
12222 // normally called parents in graph theory
12223 // these nodes <=edges= incoming nodes
12224 incomers: cache(defineDagOneHop({
12225 incoming: true
12226 }), 'incomers'),
12227 // aka DAG ancestors
12228 predecessors: defineDagAllHops({
12229 incoming: true
12230 })
12231}); // Neighbourhood functions
12232//////////////////////////
12233
12234extend(elesfn$t, {
12235 neighborhood: cache(function (selector) {
12236 var elements = [];
12237 var nodes = this.nodes();
12238
12239 for (var i = 0; i < nodes.length; i++) {
12240 // for all nodes
12241 var node = nodes[i];
12242 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12243
12244 for (var j = 0; j < connectedEdges.length; j++) {
12245 var edge = connectedEdges[j];
12246 var src = edge.source();
12247 var tgt = edge.target();
12248 var otherNode = node === src ? tgt : src; // need check in case of loop
12249
12250 if (otherNode.length > 0) {
12251 elements.push(otherNode[0]); // add node 1 hop away
12252 } // add connected edge
12253
12254
12255 elements.push(edge[0]);
12256 }
12257 }
12258
12259 return this.spawn(elements, {
12260 unique: true
12261 }).filter(selector);
12262 }, 'neighborhood'),
12263 closedNeighborhood: function closedNeighborhood(selector) {
12264 return this.neighborhood().add(this).filter(selector);
12265 },
12266 openNeighborhood: function openNeighborhood(selector) {
12267 return this.neighborhood(selector);
12268 }
12269}); // aliases
12270
12271elesfn$t.neighbourhood = elesfn$t.neighborhood;
12272elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
12273elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
12274/////////////////
12275
12276extend(elesfn$t, {
12277 source: cache(function sourceImpl(selector) {
12278 var ele = this[0];
12279 var src;
12280
12281 if (ele) {
12282 src = ele._private.source || ele.cy().collection();
12283 }
12284
12285 return src && selector ? src.filter(selector) : src;
12286 }, 'source'),
12287 target: cache(function targetImpl(selector) {
12288 var ele = this[0];
12289 var tgt;
12290
12291 if (ele) {
12292 tgt = ele._private.target || ele.cy().collection();
12293 }
12294
12295 return tgt && selector ? tgt.filter(selector) : tgt;
12296 }, 'target'),
12297 sources: defineSourceFunction({
12298 attr: 'source'
12299 }),
12300 targets: defineSourceFunction({
12301 attr: 'target'
12302 })
12303});
12304
12305function defineSourceFunction(params) {
12306 return function sourceImpl(selector) {
12307 var sources = [];
12308
12309 for (var i = 0; i < this.length; i++) {
12310 var ele = this[i];
12311 var src = ele._private[params.attr];
12312
12313 if (src) {
12314 sources.push(src);
12315 }
12316 }
12317
12318 return this.spawn(sources, {
12319 unique: true
12320 }).filter(selector);
12321 };
12322}
12323
12324extend(elesfn$t, {
12325 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
12326 edgesTo: cache(defineEdgesWithFunction({
12327 thisIsSrc: true
12328 }), 'edgesTo')
12329});
12330
12331function defineEdgesWithFunction(params) {
12332 return function edgesWithImpl(otherNodes) {
12333 var elements = [];
12334 var cy = this._private.cy;
12335 var p = params || {}; // get elements if a selector is specified
12336
12337 if (string(otherNodes)) {
12338 otherNodes = cy.$(otherNodes);
12339 }
12340
12341 for (var h = 0; h < otherNodes.length; h++) {
12342 var edges = otherNodes[h]._private.edges;
12343
12344 for (var i = 0; i < edges.length; i++) {
12345 var edge = edges[i];
12346 var edgeData = edge._private.data;
12347 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
12348 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
12349 var edgeConnectsThisAndOther = thisToOther || otherToThis;
12350
12351 if (!edgeConnectsThisAndOther) {
12352 continue;
12353 }
12354
12355 if (p.thisIsSrc || p.thisIsTgt) {
12356 if (p.thisIsSrc && !thisToOther) {
12357 continue;
12358 }
12359
12360 if (p.thisIsTgt && !otherToThis) {
12361 continue;
12362 }
12363 }
12364
12365 elements.push(edge);
12366 }
12367 }
12368
12369 return this.spawn(elements, {
12370 unique: true
12371 });
12372 };
12373}
12374
12375extend(elesfn$t, {
12376 connectedEdges: cache(function (selector) {
12377 var retEles = [];
12378 var eles = this;
12379
12380 for (var i = 0; i < eles.length; i++) {
12381 var node = eles[i];
12382
12383 if (!node.isNode()) {
12384 continue;
12385 }
12386
12387 var edges = node._private.edges;
12388
12389 for (var j = 0; j < edges.length; j++) {
12390 var edge = edges[j];
12391 retEles.push(edge);
12392 }
12393 }
12394
12395 return this.spawn(retEles, {
12396 unique: true
12397 }).filter(selector);
12398 }, 'connectedEdges'),
12399 connectedNodes: cache(function (selector) {
12400 var retEles = [];
12401 var eles = this;
12402
12403 for (var i = 0; i < eles.length; i++) {
12404 var edge = eles[i];
12405
12406 if (!edge.isEdge()) {
12407 continue;
12408 }
12409
12410 retEles.push(edge.source()[0]);
12411 retEles.push(edge.target()[0]);
12412 }
12413
12414 return this.spawn(retEles, {
12415 unique: true
12416 }).filter(selector);
12417 }, 'connectedNodes'),
12418 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
12419 codirectedEdges: cache(defineParallelEdgesFunction({
12420 codirected: true
12421 }), 'codirectedEdges')
12422});
12423
12424function defineParallelEdgesFunction(params) {
12425 var defaults = {
12426 codirected: false
12427 };
12428 params = extend({}, defaults, params);
12429 return function parallelEdgesImpl(selector) {
12430 // micro-optimised for renderer
12431 var elements = [];
12432 var edges = this.edges();
12433 var p = params; // look at all the edges in the collection
12434
12435 for (var i = 0; i < edges.length; i++) {
12436 var edge1 = edges[i];
12437 var edge1_p = edge1._private;
12438 var src1 = edge1_p.source;
12439 var srcid1 = src1._private.data.id;
12440 var tgtid1 = edge1_p.data.target;
12441 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
12442
12443 for (var j = 0; j < srcEdges1.length; j++) {
12444 var edge2 = srcEdges1[j];
12445 var edge2data = edge2._private.data;
12446 var tgtid2 = edge2data.target;
12447 var srcid2 = edge2data.source;
12448 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
12449 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
12450
12451 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
12452 elements.push(edge2);
12453 }
12454 }
12455 }
12456
12457 return this.spawn(elements, {
12458 unique: true
12459 }).filter(selector);
12460 };
12461} // Misc functions
12462/////////////////
12463
12464
12465extend(elesfn$t, {
12466 components: function components(root) {
12467 var self = this;
12468 var cy = self.cy();
12469 var visited = cy.collection();
12470 var unvisited = root == null ? self.nodes() : root.nodes();
12471 var components = [];
12472
12473 if (root != null && unvisited.empty()) {
12474 // root may contain only edges
12475 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
12476 }
12477
12478 var visitInComponent = function visitInComponent(node, component) {
12479 visited.merge(node);
12480 unvisited.unmerge(node);
12481 component.merge(node);
12482 };
12483
12484 if (unvisited.empty()) {
12485 return self.spawn();
12486 }
12487
12488 var _loop = function _loop() {
12489 // each iteration yields a component
12490 var cmpt = cy.collection();
12491 components.push(cmpt);
12492 var root = unvisited[0];
12493 visitInComponent(root, cmpt);
12494 self.bfs({
12495 directed: false,
12496 roots: root,
12497 visit: function visit(v) {
12498 return visitInComponent(v, cmpt);
12499 }
12500 });
12501 cmpt.forEach(function (node) {
12502 node.connectedEdges().forEach(function (e) {
12503 // connectedEdges() usually cached
12504 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
12505 // has() is cheap
12506 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
12507 }
12508 });
12509 });
12510 };
12511
12512 do {
12513 _loop();
12514 } while (unvisited.length > 0);
12515
12516 return components;
12517 },
12518 component: function component() {
12519 var ele = this[0];
12520 return ele.cy().mutableElements().components(ele)[0];
12521 }
12522});
12523elesfn$t.componentsOf = elesfn$t.components;
12524
12525var idFactory = {
12526 generate: function generate(cy, element, tryThisId) {
12527 var id = tryThisId != null ? tryThisId : uuid();
12528
12529 while (cy.hasElementWithId(id)) {
12530 id = uuid();
12531 }
12532
12533 return id;
12534 }
12535}; // represents a set of nodes, edges, or both together
12536
12537var Collection = function Collection(cy, elements, options) {
12538 if (cy === undefined || !core(cy)) {
12539 error('A collection must have a reference to the core');
12540 return;
12541 }
12542
12543 var map = new Map$1();
12544 var createdElements = false;
12545
12546 if (!elements) {
12547 elements = [];
12548 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
12549 createdElements = true; // make elements from json and restore all at once later
12550
12551 var eles = [];
12552 var elesIds = new Set$1();
12553
12554 for (var i = 0, l = elements.length; i < l; i++) {
12555 var json = elements[i];
12556
12557 if (json.data == null) {
12558 json.data = {};
12559 }
12560
12561 var _data = json.data; // make sure newly created elements have valid ids
12562
12563 if (_data.id == null) {
12564 _data.id = idFactory.generate(cy, json);
12565 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
12566 continue; // can't create element if prior id already exists
12567 }
12568
12569 var ele = new Element(cy, json, false);
12570 eles.push(ele);
12571 elesIds.add(_data.id);
12572 }
12573
12574 elements = eles;
12575 }
12576
12577 this.length = 0;
12578
12579 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
12580 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
12581
12582 if (element$1 == null) {
12583 continue;
12584 }
12585
12586 var id = element$1._private.data.id;
12587
12588 if (options == null || options.unique && !map.has(id)) {
12589 map.set(id, {
12590 index: this.length,
12591 ele: element$1
12592 });
12593 this[this.length] = element$1;
12594 this.length++;
12595 }
12596 }
12597
12598 this._private = {
12599 cy: cy,
12600 map: map
12601 }; // restore the elements if we created them from json
12602
12603 if (createdElements) {
12604 this.restore();
12605 }
12606}; // Functions
12607////////////////////////////////////////////////////////////////////////////////////////////////////
12608// keep the prototypes in sync (an element has the same functions as a collection)
12609// and use elefn and elesfn as shorthands to the prototypes
12610
12611
12612var elesfn$u = Element.prototype = Collection.prototype;
12613
12614elesfn$u.instanceString = function () {
12615 return 'collection';
12616};
12617
12618elesfn$u.spawn = function (cy, eles, opts) {
12619 if (!core(cy)) {
12620 // cy is optional
12621 opts = eles;
12622 eles = cy;
12623 cy = this.cy();
12624 }
12625
12626 return new Collection(cy, eles, opts);
12627};
12628
12629elesfn$u.spawnSelf = function () {
12630 return this.spawn(this);
12631};
12632
12633elesfn$u.cy = function () {
12634 return this._private.cy;
12635};
12636
12637elesfn$u.renderer = function () {
12638 return this._private.cy.renderer();
12639};
12640
12641elesfn$u.element = function () {
12642 return this[0];
12643};
12644
12645elesfn$u.collection = function () {
12646 if (collection(this)) {
12647 return this;
12648 } else {
12649 // an element
12650 return new Collection(this._private.cy, [this]);
12651 }
12652};
12653
12654elesfn$u.unique = function () {
12655 return new Collection(this._private.cy, this, {
12656 unique: true
12657 });
12658};
12659
12660elesfn$u.hasElementWithId = function (id) {
12661 id = '' + id; // id must be string
12662
12663 return this._private.map.has(id);
12664};
12665
12666elesfn$u.getElementById = function (id) {
12667 id = '' + id; // id must be string
12668
12669 var cy = this._private.cy;
12670
12671 var entry = this._private.map.get(id);
12672
12673 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
12674};
12675
12676elesfn$u.$id = elesfn$u.getElementById;
12677
12678elesfn$u.poolIndex = function () {
12679 var cy = this._private.cy;
12680 var eles = cy._private.elements;
12681 var id = this[0]._private.data.id;
12682 return eles._private.map.get(id).index;
12683};
12684
12685elesfn$u.indexOf = function (ele) {
12686 var id = ele[0]._private.data.id;
12687 return this._private.map.get(id).index;
12688};
12689
12690elesfn$u.indexOfId = function (id) {
12691 id = '' + id; // id must be string
12692
12693 return this._private.map.get(id).index;
12694};
12695
12696elesfn$u.json = function (obj) {
12697 var ele = this.element();
12698 var cy = this.cy();
12699
12700 if (ele == null && obj) {
12701 return this;
12702 } // can't set to no eles
12703
12704
12705 if (ele == null) {
12706 return undefined;
12707 } // can't get from no eles
12708
12709
12710 var p = ele._private;
12711
12712 if (plainObject(obj)) {
12713 // set
12714 cy.startBatch();
12715
12716 if (obj.data) {
12717 ele.data(obj.data);
12718 var _data2 = p.data;
12719
12720 if (ele.isEdge()) {
12721 // source and target are immutable via data()
12722 var move = false;
12723 var spec = {};
12724 var src = obj.data.source;
12725 var tgt = obj.data.target;
12726
12727 if (src != null && src != _data2.source) {
12728 spec.source = '' + src; // id must be string
12729
12730 move = true;
12731 }
12732
12733 if (tgt != null && tgt != _data2.target) {
12734 spec.target = '' + tgt; // id must be string
12735
12736 move = true;
12737 }
12738
12739 if (move) {
12740 ele = ele.move(spec);
12741 }
12742 } else {
12743 // parent is immutable via data()
12744 var parent = obj.data.parent;
12745
12746 if ((parent != null || _data2.parent != null) && parent != _data2.parent) {
12747 if (parent === undefined) {
12748 // can't set undefined imperatively, so use null
12749 parent = null;
12750 }
12751
12752 if (parent != null) {
12753 parent = '' + parent; // id must be string
12754 }
12755
12756 ele = ele.move({
12757 parent: parent
12758 });
12759 }
12760 }
12761 }
12762
12763 if (obj.position) {
12764 ele.position(obj.position);
12765 } // ignore group -- immutable
12766
12767
12768 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
12769 var obj_k = obj[k];
12770
12771 if (obj_k != null && obj_k !== p[k]) {
12772 if (obj_k) {
12773 ele[trueFnName]();
12774 } else {
12775 ele[falseFnName]();
12776 }
12777 }
12778 };
12779
12780 checkSwitch('removed', 'remove', 'restore');
12781 checkSwitch('selected', 'select', 'unselect');
12782 checkSwitch('selectable', 'selectify', 'unselectify');
12783 checkSwitch('locked', 'lock', 'unlock');
12784 checkSwitch('grabbable', 'grabify', 'ungrabify');
12785 checkSwitch('pannable', 'panify', 'unpanify');
12786
12787 if (obj.classes != null) {
12788 ele.classes(obj.classes);
12789 }
12790
12791 cy.endBatch();
12792 return this;
12793 } else if (obj === undefined) {
12794 // get
12795 var json = {
12796 data: copy(p.data),
12797 position: copy(p.position),
12798 group: p.group,
12799 removed: p.removed,
12800 selected: p.selected,
12801 selectable: p.selectable,
12802 locked: p.locked,
12803 grabbable: p.grabbable,
12804 pannable: p.pannable,
12805 classes: null
12806 };
12807 json.classes = '';
12808 var i = 0;
12809 p.classes.forEach(function (cls) {
12810 return json.classes += i++ === 0 ? cls : ' ' + cls;
12811 });
12812 return json;
12813 }
12814};
12815
12816elesfn$u.jsons = function () {
12817 var jsons = [];
12818
12819 for (var i = 0; i < this.length; i++) {
12820 var ele = this[i];
12821 var json = ele.json();
12822 jsons.push(json);
12823 }
12824
12825 return jsons;
12826};
12827
12828elesfn$u.clone = function () {
12829 var cy = this.cy();
12830 var elesArr = [];
12831
12832 for (var i = 0; i < this.length; i++) {
12833 var ele = this[i];
12834 var json = ele.json();
12835 var clone = new Element(cy, json, false); // NB no restore
12836
12837 elesArr.push(clone);
12838 }
12839
12840 return new Collection(cy, elesArr);
12841};
12842
12843elesfn$u.copy = elesfn$u.clone;
12844
12845elesfn$u.restore = function () {
12846 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12847 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12848 var self = this;
12849 var cy = self.cy();
12850 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
12851 // restore the nodes first
12852
12853 var nodes = [];
12854 var edges = [];
12855 var elements;
12856
12857 for (var _i2 = 0, l = self.length; _i2 < l; _i2++) {
12858 var ele = self[_i2];
12859
12860 if (addToPool && !ele.removed()) {
12861 // don't need to handle this ele
12862 continue;
12863 } // keep nodes first in the array and edges after
12864
12865
12866 if (ele.isNode()) {
12867 // put to front of array if node
12868 nodes.push(ele);
12869 } else {
12870 // put to end of array if edge
12871 edges.push(ele);
12872 }
12873 }
12874
12875 elements = nodes.concat(edges);
12876 var i;
12877
12878 var removeFromElements = function removeFromElements() {
12879 elements.splice(i, 1);
12880 i--;
12881 }; // now, restore each element
12882
12883
12884 for (i = 0; i < elements.length; i++) {
12885 var _ele = elements[i];
12886 var _private = _ele._private;
12887 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
12888
12889 _ele.clearTraversalCache(); // set id and validate
12890
12891
12892 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
12893 _data3.id = idFactory.generate(cy, _ele);
12894 } else if (number(_data3.id)) {
12895 _data3.id = '' + _data3.id; // now it's a string
12896 } else if (emptyString(_data3.id) || !string(_data3.id)) {
12897 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
12898
12899 removeFromElements();
12900 continue;
12901 } else if (cy.hasElementWithId(_data3.id)) {
12902 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
12903
12904 removeFromElements();
12905 continue;
12906 }
12907
12908 var id = _data3.id; // id is finalised, now let's keep a ref
12909
12910 if (_ele.isNode()) {
12911 // extra checks for nodes
12912 var pos = _private.position; // make sure the nodes have a defined position
12913
12914 if (pos.x == null) {
12915 pos.x = 0;
12916 }
12917
12918 if (pos.y == null) {
12919 pos.y = 0;
12920 }
12921 }
12922
12923 if (_ele.isEdge()) {
12924 // extra checks for edges
12925 var edge = _ele;
12926 var fields = ['source', 'target'];
12927 var fieldsLength = fields.length;
12928 var badSourceOrTarget = false;
12929
12930 for (var j = 0; j < fieldsLength; j++) {
12931 var field = fields[j];
12932 var val = _data3[field];
12933
12934 if (number(val)) {
12935 val = _data3[field] = '' + _data3[field]; // now string
12936 }
12937
12938 if (val == null || val === '') {
12939 // can't create if source or target is not defined properly
12940 error('Can not create edge `' + id + '` with unspecified ' + field);
12941 badSourceOrTarget = true;
12942 } else if (!cy.hasElementWithId(val)) {
12943 // can't create edge if one of its nodes doesn't exist
12944 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
12945 badSourceOrTarget = true;
12946 }
12947 }
12948
12949 if (badSourceOrTarget) {
12950 removeFromElements();
12951 continue;
12952 } // can't create this
12953
12954
12955 var src = cy.getElementById(_data3.source);
12956 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
12957
12958 if (src.same(tgt)) {
12959 src._private.edges.push(edge);
12960 } else {
12961 src._private.edges.push(edge);
12962
12963 tgt._private.edges.push(edge);
12964 }
12965
12966 edge._private.source = src;
12967 edge._private.target = tgt;
12968 } // if is edge
12969 // create mock ids / indexes maps for element so it can be used like collections
12970
12971
12972 _private.map = new Map$1();
12973
12974 _private.map.set(id, {
12975 ele: _ele,
12976 index: 0
12977 });
12978
12979 _private.removed = false;
12980
12981 if (addToPool) {
12982 cy.addToPool(_ele);
12983 }
12984 } // for each element
12985 // do compound node sanity checks
12986
12987
12988 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
12989 // each node
12990 var node = nodes[_i3];
12991 var _data4 = node._private.data;
12992
12993 if (number(_data4.parent)) {
12994 // then automake string
12995 _data4.parent = '' + _data4.parent;
12996 }
12997
12998 var parentId = _data4.parent;
12999 var specifiedParent = parentId != null;
13000
13001 if (specifiedParent) {
13002 var parent = cy.getElementById(parentId);
13003
13004 if (parent.empty()) {
13005 // non-existant parent; just remove it
13006 _data4.parent = undefined;
13007 } else {
13008 var selfAsParent = false;
13009 var ancestor = parent;
13010
13011 while (!ancestor.empty()) {
13012 if (node.same(ancestor)) {
13013 // mark self as parent and remove from data
13014 selfAsParent = true;
13015 _data4.parent = undefined; // remove parent reference
13016 // exit or we loop forever
13017
13018 break;
13019 }
13020
13021 ancestor = ancestor.parent();
13022 }
13023
13024 if (!selfAsParent) {
13025 // connect with children
13026 parent[0]._private.children.push(node);
13027
13028 node._private.parent = parent[0]; // let the core know we have a compound graph
13029
13030 cy_p.hasCompoundNodes = true;
13031 }
13032 } // else
13033
13034 } // if specified parent
13035
13036 } // for each node
13037
13038
13039 if (elements.length > 0) {
13040 var restored = new Collection(cy, elements);
13041
13042 for (var _i4 = 0; _i4 < restored.length; _i4++) {
13043 var _ele2 = restored[_i4];
13044
13045 if (_ele2.isNode()) {
13046 continue;
13047 } // adding an edge invalidates the traversal caches for the parallel edges
13048
13049
13050 _ele2.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13051
13052
13053 _ele2.source().clearTraversalCache();
13054
13055 _ele2.target().clearTraversalCache();
13056 }
13057
13058 var toUpdateStyle;
13059
13060 if (cy_p.hasCompoundNodes) {
13061 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13062 } else {
13063 toUpdateStyle = restored;
13064 }
13065
13066 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13067
13068 if (notifyRenderer) {
13069 restored.emitAndNotify('add');
13070 } else if (addToPool) {
13071 restored.emit('add');
13072 }
13073 }
13074
13075 return self; // chainability
13076};
13077
13078elesfn$u.removed = function () {
13079 var ele = this[0];
13080 return ele && ele._private.removed;
13081};
13082
13083elesfn$u.inside = function () {
13084 var ele = this[0];
13085 return ele && !ele._private.removed;
13086};
13087
13088elesfn$u.remove = function () {
13089 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13090 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13091 var self = this;
13092 var elesToRemove = [];
13093 var elesToRemoveIds = {};
13094 var cy = self._private.cy; // add connected edges
13095
13096 function addConnectedEdges(node) {
13097 var edges = node._private.edges;
13098
13099 for (var i = 0; i < edges.length; i++) {
13100 add(edges[i]);
13101 }
13102 } // add descendant nodes
13103
13104
13105 function addChildren(node) {
13106 var children = node._private.children;
13107
13108 for (var i = 0; i < children.length; i++) {
13109 add(children[i]);
13110 }
13111 }
13112
13113 function add(ele) {
13114 var alreadyAdded = elesToRemoveIds[ele.id()];
13115
13116 if (removeFromPool && ele.removed() || alreadyAdded) {
13117 return;
13118 } else {
13119 elesToRemoveIds[ele.id()] = true;
13120 }
13121
13122 if (ele.isNode()) {
13123 elesToRemove.push(ele); // nodes are removed last
13124
13125 addConnectedEdges(ele);
13126 addChildren(ele);
13127 } else {
13128 elesToRemove.unshift(ele); // edges are removed first
13129 }
13130 } // make the list of elements to remove
13131 // (may be removing more than specified due to connected edges etc)
13132
13133
13134 for (var i = 0, l = self.length; i < l; i++) {
13135 var ele = self[i];
13136 add(ele);
13137 }
13138
13139 function removeEdgeRef(node, edge) {
13140 var connectedEdges = node._private.edges;
13141 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13142
13143 node.clearTraversalCache();
13144 }
13145
13146 function removeParallelRef(pllEdge) {
13147 // removing an edge invalidates the traversal caches for the parallel edges
13148 pllEdge.clearTraversalCache();
13149 }
13150
13151 var alteredParents = [];
13152 alteredParents.ids = {};
13153
13154 function removeChildRef(parent, ele) {
13155 ele = ele[0];
13156 parent = parent[0];
13157 var children = parent._private.children;
13158 var pid = parent.id();
13159 removeFromArray(children, ele); // remove parent => child ref
13160
13161 ele._private.parent = null; // remove child => parent ref
13162
13163 if (!alteredParents.ids[pid]) {
13164 alteredParents.ids[pid] = true;
13165 alteredParents.push(parent);
13166 }
13167 }
13168
13169 self.dirtyCompoundBoundsCache();
13170
13171 if (removeFromPool) {
13172 cy.removeFromPool(elesToRemove); // remove from core pool
13173 }
13174
13175 for (var _i5 = 0; _i5 < elesToRemove.length; _i5++) {
13176 var _ele3 = elesToRemove[_i5];
13177
13178 if (_ele3.isEdge()) {
13179 // remove references to this edge in its connected nodes
13180 var src = _ele3.source()[0];
13181
13182 var tgt = _ele3.target()[0];
13183
13184 removeEdgeRef(src, _ele3);
13185 removeEdgeRef(tgt, _ele3);
13186
13187 var pllEdges = _ele3.parallelEdges();
13188
13189 for (var j = 0; j < pllEdges.length; j++) {
13190 var pllEdge = pllEdges[j];
13191 removeParallelRef(pllEdge);
13192
13193 if (pllEdge.isBundledBezier()) {
13194 pllEdge.dirtyBoundingBoxCache();
13195 }
13196 }
13197 } else {
13198 // remove reference to parent
13199 var parent = _ele3.parent();
13200
13201 if (parent.length !== 0) {
13202 removeChildRef(parent, _ele3);
13203 }
13204 }
13205
13206 if (removeFromPool) {
13207 // mark as removed
13208 _ele3._private.removed = true;
13209 }
13210 } // check to see if we have a compound graph or not
13211
13212
13213 var elesStillInside = cy._private.elements;
13214 cy._private.hasCompoundNodes = false;
13215
13216 for (var _i6 = 0; _i6 < elesStillInside.length; _i6++) {
13217 var _ele4 = elesStillInside[_i6];
13218
13219 if (_ele4.isParent()) {
13220 cy._private.hasCompoundNodes = true;
13221 break;
13222 }
13223 }
13224
13225 var removedElements = new Collection(this.cy(), elesToRemove);
13226
13227 if (removedElements.size() > 0) {
13228 // must manually notify since trigger won't do this automatically once removed
13229 if (notifyRenderer) {
13230 removedElements.emitAndNotify('remove');
13231 } else if (removeFromPool) {
13232 removedElements.emit('remove');
13233 }
13234 } // the parents who were modified by the removal need their style updated
13235
13236
13237 for (var _i7 = 0; _i7 < alteredParents.length; _i7++) {
13238 var _ele5 = alteredParents[_i7];
13239
13240 if (!removeFromPool || !_ele5.removed()) {
13241 _ele5.updateStyle();
13242 }
13243 }
13244
13245 return removedElements;
13246};
13247
13248elesfn$u.move = function (struct) {
13249 var cy = this._private.cy;
13250 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
13251 // (our calls to remove/restore do not remove from the graph or make events)
13252
13253 var notifyRenderer = false;
13254 var modifyPool = false;
13255
13256 var toString = function toString(id) {
13257 return id == null ? id : '' + id;
13258 }; // id must be string
13259
13260
13261 if (struct.source !== undefined || struct.target !== undefined) {
13262 var srcId = toString(struct.source);
13263 var tgtId = toString(struct.target);
13264 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13265 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13266
13267 if (srcExists || tgtExists) {
13268 cy.batch(function () {
13269 // avoid duplicate style updates
13270 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13271
13272 eles.emitAndNotify('moveout');
13273
13274 for (var i = 0; i < eles.length; i++) {
13275 var ele = eles[i];
13276 var _data5 = ele._private.data;
13277
13278 if (ele.isEdge()) {
13279 if (srcExists) {
13280 _data5.source = srcId;
13281 }
13282
13283 if (tgtExists) {
13284 _data5.target = tgtId;
13285 }
13286 }
13287 }
13288
13289 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13290 });
13291 eles.emitAndNotify('move');
13292 }
13293 } else if (struct.parent !== undefined) {
13294 // move node to new parent
13295 var parentId = toString(struct.parent);
13296 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13297
13298 if (parentExists) {
13299 var pidToAssign = parentId === null ? undefined : parentId;
13300 cy.batch(function () {
13301 // avoid duplicate style updates
13302 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13303
13304 updated.emitAndNotify('moveout');
13305
13306 for (var i = 0; i < eles.length; i++) {
13307 var ele = eles[i];
13308 var _data6 = ele._private.data;
13309
13310 if (ele.isNode()) {
13311 _data6.parent = pidToAssign;
13312 }
13313 }
13314
13315 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13316 });
13317 eles.emitAndNotify('move');
13318 }
13319 }
13320
13321 return this;
13322};
13323
13324[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) {
13325 extend(elesfn$u, props);
13326});
13327
13328var corefn = {
13329 add: function add(opts) {
13330 var elements;
13331 var cy = this; // add the elements
13332
13333 if (elementOrCollection(opts)) {
13334 var eles = opts;
13335
13336 if (eles._private.cy === cy) {
13337 // same instance => just restore
13338 elements = eles.restore();
13339 } else {
13340 // otherwise, copy from json
13341 var jsons = [];
13342
13343 for (var i = 0; i < eles.length; i++) {
13344 var ele = eles[i];
13345 jsons.push(ele.json());
13346 }
13347
13348 elements = new Collection(cy, jsons);
13349 }
13350 } // specify an array of options
13351 else if (array(opts)) {
13352 var _jsons = opts;
13353 elements = new Collection(cy, _jsons);
13354 } // specify via opts.nodes and opts.edges
13355 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13356 var elesByGroup = opts;
13357 var _jsons2 = [];
13358 var grs = ['nodes', 'edges'];
13359
13360 for (var _i = 0, il = grs.length; _i < il; _i++) {
13361 var group = grs[_i];
13362 var elesArray = elesByGroup[group];
13363
13364 if (array(elesArray)) {
13365 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13366 var json = extend({
13367 group: group
13368 }, elesArray[j]);
13369
13370 _jsons2.push(json);
13371 }
13372 }
13373 }
13374
13375 elements = new Collection(cy, _jsons2);
13376 } // specify options for one element
13377 else {
13378 var _json = opts;
13379 elements = new Element(cy, _json).collection();
13380 }
13381
13382 return elements;
13383 },
13384 remove: function remove(collection) {
13385 if (elementOrCollection(collection)) ; else if (string(collection)) {
13386 var selector = collection;
13387 collection = this.$(selector);
13388 }
13389
13390 return collection.remove();
13391 }
13392};
13393
13394/* global Float32Array */
13395
13396/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13397function generateCubicBezier(mX1, mY1, mX2, mY2) {
13398 var NEWTON_ITERATIONS = 4,
13399 NEWTON_MIN_SLOPE = 0.001,
13400 SUBDIVISION_PRECISION = 0.0000001,
13401 SUBDIVISION_MAX_ITERATIONS = 10,
13402 kSplineTableSize = 11,
13403 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
13404 float32ArraySupported = typeof Float32Array !== 'undefined';
13405 /* Must contain four arguments. */
13406
13407 if (arguments.length !== 4) {
13408 return false;
13409 }
13410 /* Arguments must be numbers. */
13411
13412
13413 for (var i = 0; i < 4; ++i) {
13414 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
13415 return false;
13416 }
13417 }
13418 /* X values must be in the [0, 1] range. */
13419
13420
13421 mX1 = Math.min(mX1, 1);
13422 mX2 = Math.min(mX2, 1);
13423 mX1 = Math.max(mX1, 0);
13424 mX2 = Math.max(mX2, 0);
13425 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
13426
13427 function A(aA1, aA2) {
13428 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
13429 }
13430
13431 function B(aA1, aA2) {
13432 return 3.0 * aA2 - 6.0 * aA1;
13433 }
13434
13435 function C(aA1) {
13436 return 3.0 * aA1;
13437 }
13438
13439 function calcBezier(aT, aA1, aA2) {
13440 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
13441 }
13442
13443 function getSlope(aT, aA1, aA2) {
13444 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
13445 }
13446
13447 function newtonRaphsonIterate(aX, aGuessT) {
13448 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
13449 var currentSlope = getSlope(aGuessT, mX1, mX2);
13450
13451 if (currentSlope === 0.0) {
13452 return aGuessT;
13453 }
13454
13455 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
13456 aGuessT -= currentX / currentSlope;
13457 }
13458
13459 return aGuessT;
13460 }
13461
13462 function calcSampleValues() {
13463 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
13464 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
13465 }
13466 }
13467
13468 function binarySubdivide(aX, aA, aB) {
13469 var currentX,
13470 currentT,
13471 i = 0;
13472
13473 do {
13474 currentT = aA + (aB - aA) / 2.0;
13475 currentX = calcBezier(currentT, mX1, mX2) - aX;
13476
13477 if (currentX > 0.0) {
13478 aB = currentT;
13479 } else {
13480 aA = currentT;
13481 }
13482 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
13483
13484 return currentT;
13485 }
13486
13487 function getTForX(aX) {
13488 var intervalStart = 0.0,
13489 currentSample = 1,
13490 lastSample = kSplineTableSize - 1;
13491
13492 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
13493 intervalStart += kSampleStepSize;
13494 }
13495
13496 --currentSample;
13497 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
13498 guessForT = intervalStart + dist * kSampleStepSize,
13499 initialSlope = getSlope(guessForT, mX1, mX2);
13500
13501 if (initialSlope >= NEWTON_MIN_SLOPE) {
13502 return newtonRaphsonIterate(aX, guessForT);
13503 } else if (initialSlope === 0.0) {
13504 return guessForT;
13505 } else {
13506 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
13507 }
13508 }
13509
13510 var _precomputed = false;
13511
13512 function precompute() {
13513 _precomputed = true;
13514
13515 if (mX1 !== mY1 || mX2 !== mY2) {
13516 calcSampleValues();
13517 }
13518 }
13519
13520 var f = function f(aX) {
13521 if (!_precomputed) {
13522 precompute();
13523 }
13524
13525 if (mX1 === mY1 && mX2 === mY2) {
13526 return aX;
13527 }
13528
13529 if (aX === 0) {
13530 return 0;
13531 }
13532
13533 if (aX === 1) {
13534 return 1;
13535 }
13536
13537 return calcBezier(getTForX(aX), mY1, mY2);
13538 };
13539
13540 f.getControlPoints = function () {
13541 return [{
13542 x: mX1,
13543 y: mY1
13544 }, {
13545 x: mX2,
13546 y: mY2
13547 }];
13548 };
13549
13550 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
13551
13552 f.toString = function () {
13553 return str;
13554 };
13555
13556 return f;
13557}
13558
13559/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13560
13561/* 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
13562 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
13563var generateSpringRK4 = function () {
13564 function springAccelerationForState(state) {
13565 return -state.tension * state.x - state.friction * state.v;
13566 }
13567
13568 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
13569 var state = {
13570 x: initialState.x + derivative.dx * dt,
13571 v: initialState.v + derivative.dv * dt,
13572 tension: initialState.tension,
13573 friction: initialState.friction
13574 };
13575 return {
13576 dx: state.v,
13577 dv: springAccelerationForState(state)
13578 };
13579 }
13580
13581 function springIntegrateState(state, dt) {
13582 var a = {
13583 dx: state.v,
13584 dv: springAccelerationForState(state)
13585 },
13586 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
13587 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
13588 d = springEvaluateStateWithDerivative(state, dt, c),
13589 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
13590 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
13591 state.x = state.x + dxdt * dt;
13592 state.v = state.v + dvdt * dt;
13593 return state;
13594 }
13595
13596 return function springRK4Factory(tension, friction, duration) {
13597 var initState = {
13598 x: -1,
13599 v: 0,
13600 tension: null,
13601 friction: null
13602 },
13603 path = [0],
13604 time_lapsed = 0,
13605 tolerance = 1 / 10000,
13606 DT = 16 / 1000,
13607 have_duration,
13608 dt,
13609 last_state;
13610 tension = parseFloat(tension) || 500;
13611 friction = parseFloat(friction) || 20;
13612 duration = duration || null;
13613 initState.tension = tension;
13614 initState.friction = friction;
13615 have_duration = duration !== null;
13616 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
13617
13618 if (have_duration) {
13619 /* Run the simulation without a duration. */
13620 time_lapsed = springRK4Factory(tension, friction);
13621 /* Compute the adjusted time delta. */
13622
13623 dt = time_lapsed / duration * DT;
13624 } else {
13625 dt = DT;
13626 }
13627
13628 for (;;) {
13629 /* Next/step function .*/
13630 last_state = springIntegrateState(last_state || initState, dt);
13631 /* Store the position. */
13632
13633 path.push(1 + last_state.x);
13634 time_lapsed += 16;
13635 /* If the change threshold is reached, break. */
13636
13637 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
13638 break;
13639 }
13640 }
13641 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
13642 computed path and returns a snapshot of the position according to a given percentComplete. */
13643
13644
13645 return !have_duration ? time_lapsed : function (percentComplete) {
13646 return path[percentComplete * (path.length - 1) | 0];
13647 };
13648 };
13649}();
13650
13651var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
13652 var bezier = generateCubicBezier(t1, p1, t2, p2);
13653 return function (start, end, percent) {
13654 return start + (end - start) * bezier(percent);
13655 };
13656};
13657
13658var easings = {
13659 'linear': function linear(start, end, percent) {
13660 return start + (end - start) * percent;
13661 },
13662 // default easings
13663 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
13664 'ease-in': cubicBezier(0.42, 0, 1, 1),
13665 'ease-out': cubicBezier(0, 0, 0.58, 1),
13666 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
13667 // sine
13668 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
13669 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
13670 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
13671 // quad
13672 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
13673 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
13674 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
13675 // cubic
13676 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
13677 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
13678 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
13679 // quart
13680 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
13681 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
13682 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
13683 // quint
13684 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
13685 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
13686 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
13687 // expo
13688 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
13689 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
13690 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
13691 // circ
13692 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
13693 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
13694 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
13695 // user param easings...
13696 'spring': function spring(tension, friction, duration) {
13697 if (duration === 0) {
13698 // can't get a spring w/ duration 0
13699 return easings.linear; // duration 0 => jump to end so impl doesn't matter
13700 }
13701
13702 var spring = generateSpringRK4(tension, friction, duration);
13703 return function (start, end, percent) {
13704 return start + (end - start) * spring(percent);
13705 };
13706 },
13707 'cubic-bezier': cubicBezier
13708};
13709
13710function getEasedValue(type, start, end, percent, easingFn) {
13711 if (percent === 1) {
13712 return end;
13713 }
13714
13715 if (start === end) {
13716 return end;
13717 }
13718
13719 var val = easingFn(start, end, percent);
13720
13721 if (type == null) {
13722 return val;
13723 }
13724
13725 if (type.roundValue || type.color) {
13726 val = Math.round(val);
13727 }
13728
13729 if (type.min !== undefined) {
13730 val = Math.max(val, type.min);
13731 }
13732
13733 if (type.max !== undefined) {
13734 val = Math.min(val, type.max);
13735 }
13736
13737 return val;
13738}
13739
13740function getValue(prop, spec) {
13741 if (prop.pfValue != null || prop.value != null) {
13742 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
13743 return prop.pfValue;
13744 } else {
13745 return prop.value;
13746 }
13747 } else {
13748 return prop;
13749 }
13750}
13751
13752function ease(startProp, endProp, percent, easingFn, propSpec) {
13753 var type = propSpec != null ? propSpec.type : null;
13754
13755 if (percent < 0) {
13756 percent = 0;
13757 } else if (percent > 1) {
13758 percent = 1;
13759 }
13760
13761 var start = getValue(startProp, propSpec);
13762 var end = getValue(endProp, propSpec);
13763
13764 if (number(start) && number(end)) {
13765 return getEasedValue(type, start, end, percent, easingFn);
13766 } else if (array(start) && array(end)) {
13767 var easedArr = [];
13768
13769 for (var i = 0; i < end.length; i++) {
13770 var si = start[i];
13771 var ei = end[i];
13772
13773 if (si != null && ei != null) {
13774 var val = getEasedValue(type, si, ei, percent, easingFn);
13775 easedArr.push(val);
13776 } else {
13777 easedArr.push(ei);
13778 }
13779 }
13780
13781 return easedArr;
13782 }
13783
13784 return undefined;
13785}
13786
13787function step(self, ani, now, isCore) {
13788 var isEles = !isCore;
13789 var _p = self._private;
13790 var ani_p = ani._private;
13791 var pEasing = ani_p.easing;
13792 var startTime = ani_p.startTime;
13793 var cy = isCore ? self : self.cy();
13794 var style = cy.style();
13795
13796 if (!ani_p.easingImpl) {
13797 if (pEasing == null) {
13798 // use default
13799 ani_p.easingImpl = easings['linear'];
13800 } else {
13801 // then define w/ name
13802 var easingVals;
13803
13804 if (string(pEasing)) {
13805 var easingProp = style.parse('transition-timing-function', pEasing);
13806 easingVals = easingProp.value;
13807 } else {
13808 // then assume preparsed array
13809 easingVals = pEasing;
13810 }
13811
13812 var name, args;
13813
13814 if (string(easingVals)) {
13815 name = easingVals;
13816 args = [];
13817 } else {
13818 name = easingVals[1];
13819 args = easingVals.slice(2).map(function (n) {
13820 return +n;
13821 });
13822 }
13823
13824 if (args.length > 0) {
13825 // create with args
13826 if (name === 'spring') {
13827 args.push(ani_p.duration); // need duration to generate spring
13828 }
13829
13830 ani_p.easingImpl = easings[name].apply(null, args);
13831 } else {
13832 // static impl by name
13833 ani_p.easingImpl = easings[name];
13834 }
13835 }
13836 }
13837
13838 var easing = ani_p.easingImpl;
13839 var percent;
13840
13841 if (ani_p.duration === 0) {
13842 percent = 1;
13843 } else {
13844 percent = (now - startTime) / ani_p.duration;
13845 }
13846
13847 if (ani_p.applying) {
13848 percent = ani_p.progress;
13849 }
13850
13851 if (percent < 0) {
13852 percent = 0;
13853 } else if (percent > 1) {
13854 percent = 1;
13855 }
13856
13857 if (ani_p.delay == null) {
13858 // then update
13859 var startPos = ani_p.startPosition;
13860 var endPos = ani_p.position;
13861
13862 if (endPos && isEles && !self.locked()) {
13863 var newPos = {};
13864
13865 if (valid(startPos.x, endPos.x)) {
13866 newPos.x = ease(startPos.x, endPos.x, percent, easing);
13867 }
13868
13869 if (valid(startPos.y, endPos.y)) {
13870 newPos.y = ease(startPos.y, endPos.y, percent, easing);
13871 }
13872
13873 self.position(newPos);
13874 }
13875
13876 var startPan = ani_p.startPan;
13877 var endPan = ani_p.pan;
13878 var pan = _p.pan;
13879 var animatingPan = endPan != null && isCore;
13880
13881 if (animatingPan) {
13882 if (valid(startPan.x, endPan.x)) {
13883 pan.x = ease(startPan.x, endPan.x, percent, easing);
13884 }
13885
13886 if (valid(startPan.y, endPan.y)) {
13887 pan.y = ease(startPan.y, endPan.y, percent, easing);
13888 }
13889
13890 self.emit('pan');
13891 }
13892
13893 var startZoom = ani_p.startZoom;
13894 var endZoom = ani_p.zoom;
13895 var animatingZoom = endZoom != null && isCore;
13896
13897 if (animatingZoom) {
13898 if (valid(startZoom, endZoom)) {
13899 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
13900 }
13901
13902 self.emit('zoom');
13903 }
13904
13905 if (animatingPan || animatingZoom) {
13906 self.emit('viewport');
13907 }
13908
13909 var props = ani_p.style;
13910
13911 if (props && props.length > 0 && isEles) {
13912 for (var i = 0; i < props.length; i++) {
13913 var prop = props[i];
13914 var _name = prop.name;
13915 var end = prop;
13916 var start = ani_p.startStyle[_name];
13917 var propSpec = style.properties[start.name];
13918 var easedVal = ease(start, end, percent, easing, propSpec);
13919 style.overrideBypass(self, _name, easedVal);
13920 } // for props
13921
13922
13923 self.emit('style');
13924 } // if
13925
13926 }
13927
13928 ani_p.progress = percent;
13929 return percent;
13930}
13931
13932function valid(start, end) {
13933 if (start == null || end == null) {
13934 return false;
13935 }
13936
13937 if (number(start) && number(end)) {
13938 return true;
13939 } else if (start && end) {
13940 return true;
13941 }
13942
13943 return false;
13944}
13945
13946function startAnimation(self, ani, now, isCore) {
13947 var ani_p = ani._private;
13948 ani_p.started = true;
13949 ani_p.startTime = now - ani_p.progress * ani_p.duration;
13950}
13951
13952function stepAll(now, cy) {
13953 var eles = cy._private.aniEles;
13954 var doneEles = [];
13955
13956 function stepOne(ele, isCore) {
13957 var _p = ele._private;
13958 var current = _p.animation.current;
13959 var queue = _p.animation.queue;
13960 var ranAnis = false; // cancel all animations on display:none ele
13961
13962 if (!isCore && ele.pstyle('display').value === 'none') {
13963 // put all current and queue animations in this tick's current list
13964 // and empty the lists for the element
13965 current = current.splice(0, current.length).concat(queue.splice(0, queue.length)); // stop all animations
13966
13967 for (var i = 0; i < current.length; i++) {
13968 current[i].stop();
13969 }
13970 } // if nothing currently animating, get something from the queue
13971
13972
13973 if (current.length === 0) {
13974 var next = queue.shift();
13975
13976 if (next) {
13977 current.push(next);
13978 }
13979 }
13980
13981 var callbacks = function callbacks(_callbacks) {
13982 for (var j = _callbacks.length - 1; j >= 0; j--) {
13983 var cb = _callbacks[j];
13984 cb();
13985 }
13986
13987 _callbacks.splice(0, _callbacks.length);
13988 }; // step and remove if done
13989
13990
13991 for (var _i = current.length - 1; _i >= 0; _i--) {
13992 var ani = current[_i];
13993 var ani_p = ani._private;
13994
13995 if (ani_p.stopped) {
13996 current.splice(_i, 1);
13997 ani_p.hooked = false;
13998 ani_p.playing = false;
13999 ani_p.started = false;
14000 callbacks(ani_p.frames);
14001 continue;
14002 }
14003
14004 if (!ani_p.playing && !ani_p.applying) {
14005 continue;
14006 } // an apply() while playing shouldn't do anything
14007
14008
14009 if (ani_p.playing && ani_p.applying) {
14010 ani_p.applying = false;
14011 }
14012
14013 if (!ani_p.started) {
14014 startAnimation(ele, ani, now);
14015 }
14016
14017 step(ele, ani, now, isCore);
14018
14019 if (ani_p.applying) {
14020 ani_p.applying = false;
14021 }
14022
14023 callbacks(ani_p.frames);
14024
14025 if (ani_p.step != null) {
14026 ani_p.step(now);
14027 }
14028
14029 if (ani.completed()) {
14030 current.splice(_i, 1);
14031 ani_p.hooked = false;
14032 ani_p.playing = false;
14033 ani_p.started = false;
14034 callbacks(ani_p.completes);
14035 }
14036
14037 ranAnis = true;
14038 }
14039
14040 if (!isCore && current.length === 0 && queue.length === 0) {
14041 doneEles.push(ele);
14042 }
14043
14044 return ranAnis;
14045 } // stepElement
14046 // handle all eles
14047
14048
14049 var ranEleAni = false;
14050
14051 for (var e = 0; e < eles.length; e++) {
14052 var ele = eles[e];
14053 var handledThisEle = stepOne(ele);
14054 ranEleAni = ranEleAni || handledThisEle;
14055 } // each element
14056
14057
14058 var ranCoreAni = stepOne(cy, true); // notify renderer
14059
14060 if (ranEleAni || ranCoreAni) {
14061 if (eles.length > 0) {
14062 cy.notify('draw', eles);
14063 } else {
14064 cy.notify('draw');
14065 }
14066 } // remove elements from list of currently animating if its queues are empty
14067
14068
14069 eles.unmerge(doneEles);
14070 cy.emit('step');
14071} // stepAll
14072
14073var corefn$1 = {
14074 // pull in animation functions
14075 animate: define$3.animate(),
14076 animation: define$3.animation(),
14077 animated: define$3.animated(),
14078 clearQueue: define$3.clearQueue(),
14079 delay: define$3.delay(),
14080 delayAnimation: define$3.delayAnimation(),
14081 stop: define$3.stop(),
14082 addToAnimationPool: function addToAnimationPool(eles) {
14083 var cy = this;
14084
14085 if (!cy.styleEnabled()) {
14086 return;
14087 } // save cycles when no style used
14088
14089
14090 cy._private.aniEles.merge(eles);
14091 },
14092 stopAnimationLoop: function stopAnimationLoop() {
14093 this._private.animationsRunning = false;
14094 },
14095 startAnimationLoop: function startAnimationLoop() {
14096 var cy = this;
14097 cy._private.animationsRunning = true;
14098
14099 if (!cy.styleEnabled()) {
14100 return;
14101 } // save cycles when no style used
14102 // NB the animation loop will exec in headless environments if style enabled
14103 // and explicit cy.destroy() is necessary to stop the loop
14104
14105
14106 function headlessStep() {
14107 if (!cy._private.animationsRunning) {
14108 return;
14109 }
14110
14111 requestAnimationFrame(function animationStep(now) {
14112 stepAll(now, cy);
14113 headlessStep();
14114 });
14115 }
14116
14117 var renderer = cy.renderer();
14118
14119 if (renderer && renderer.beforeRender) {
14120 // let the renderer schedule animations
14121 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14122 stepAll(now, cy);
14123 }, renderer.beforeRenderPriorities.animations);
14124 } else {
14125 // manage the animation loop ourselves
14126 headlessStep(); // first call
14127 }
14128 }
14129};
14130
14131var emitterOptions$1 = {
14132 qualifierCompare: function qualifierCompare(selector1, selector2) {
14133 if (selector1 == null || selector2 == null) {
14134 return selector1 == null && selector2 == null;
14135 } else {
14136 return selector1.sameText(selector2);
14137 }
14138 },
14139 eventMatches: function eventMatches(cy, listener, eventObj) {
14140 var selector = listener.qualifier;
14141
14142 if (selector != null) {
14143 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14144 }
14145
14146 return true;
14147 },
14148 addEventFields: function addEventFields(cy, evt) {
14149 evt.cy = cy;
14150 evt.target = cy;
14151 },
14152 callbackContext: function callbackContext(cy, listener, eventObj) {
14153 return listener.qualifier != null ? eventObj.target : cy;
14154 }
14155};
14156
14157var argSelector$1 = function argSelector(arg) {
14158 if (string(arg)) {
14159 return new Selector(arg);
14160 } else {
14161 return arg;
14162 }
14163};
14164
14165var elesfn$v = {
14166 createEmitter: function createEmitter() {
14167 var _p = this._private;
14168
14169 if (!_p.emitter) {
14170 _p.emitter = new Emitter(emitterOptions$1, this);
14171 }
14172
14173 return this;
14174 },
14175 emitter: function emitter() {
14176 return this._private.emitter;
14177 },
14178 on: function on(events, selector, callback) {
14179 this.emitter().on(events, argSelector$1(selector), callback);
14180 return this;
14181 },
14182 removeListener: function removeListener(events, selector, callback) {
14183 this.emitter().removeListener(events, argSelector$1(selector), callback);
14184 return this;
14185 },
14186 removeAllListeners: function removeAllListeners() {
14187 this.emitter().removeAllListeners();
14188 return this;
14189 },
14190 one: function one(events, selector, callback) {
14191 this.emitter().one(events, argSelector$1(selector), callback);
14192 return this;
14193 },
14194 once: function once(events, selector, callback) {
14195 this.emitter().one(events, argSelector$1(selector), callback);
14196 return this;
14197 },
14198 emit: function emit(events, extraParams) {
14199 this.emitter().emit(events, extraParams);
14200 return this;
14201 },
14202 emitAndNotify: function emitAndNotify(event, eles) {
14203 this.emit(event);
14204 this.notify(event, eles);
14205 return this;
14206 }
14207};
14208define$3.eventAliasesOn(elesfn$v);
14209
14210var corefn$2 = {
14211 png: function png(options) {
14212 var renderer = this._private.renderer;
14213 options = options || {};
14214 return renderer.png(options);
14215 },
14216 jpg: function jpg(options) {
14217 var renderer = this._private.renderer;
14218 options = options || {};
14219 options.bg = options.bg || '#fff';
14220 return renderer.jpg(options);
14221 }
14222};
14223corefn$2.jpeg = corefn$2.jpg;
14224
14225var corefn$3 = {
14226 layout: function layout(options) {
14227 var cy = this;
14228
14229 if (options == null) {
14230 error('Layout options must be specified to make a layout');
14231 return;
14232 }
14233
14234 if (options.name == null) {
14235 error('A `name` must be specified to make a layout');
14236 return;
14237 }
14238
14239 var name = options.name;
14240 var Layout = cy.extension('layout', name);
14241
14242 if (Layout == null) {
14243 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14244 return;
14245 }
14246
14247 var eles;
14248
14249 if (string(options.eles)) {
14250 eles = cy.$(options.eles);
14251 } else {
14252 eles = options.eles != null ? options.eles : cy.$();
14253 }
14254
14255 var layout = new Layout(extend({}, options, {
14256 cy: cy,
14257 eles: eles
14258 }));
14259 return layout;
14260 }
14261};
14262corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
14263
14264var corefn$4 = {
14265 notify: function notify(eventName, eventEles) {
14266 var _p = this._private;
14267
14268 if (this.batching()) {
14269 _p.batchNotifications = _p.batchNotifications || {};
14270 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14271
14272 if (eventEles != null) {
14273 eles.merge(eventEles);
14274 }
14275
14276 return; // notifications are disabled during batching
14277 }
14278
14279 if (!_p.notificationsEnabled) {
14280 return;
14281 } // exit on disabled
14282
14283
14284 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
14285
14286 if (this.destroyed() || !renderer) {
14287 return;
14288 }
14289
14290 renderer.notify(eventName, eventEles);
14291 },
14292 notifications: function notifications(bool) {
14293 var p = this._private;
14294
14295 if (bool === undefined) {
14296 return p.notificationsEnabled;
14297 } else {
14298 p.notificationsEnabled = bool ? true : false;
14299 }
14300
14301 return this;
14302 },
14303 noNotifications: function noNotifications(callback) {
14304 this.notifications(false);
14305 callback();
14306 this.notifications(true);
14307 },
14308 batching: function batching() {
14309 return this._private.batchCount > 0;
14310 },
14311 startBatch: function startBatch() {
14312 var _p = this._private;
14313
14314 if (_p.batchCount == null) {
14315 _p.batchCount = 0;
14316 }
14317
14318 if (_p.batchCount === 0) {
14319 _p.batchStyleEles = this.collection();
14320 _p.batchNotifications = {};
14321 }
14322
14323 _p.batchCount++;
14324 return this;
14325 },
14326 endBatch: function endBatch() {
14327 var _p = this._private;
14328
14329 if (_p.batchCount === 0) {
14330 return this;
14331 }
14332
14333 _p.batchCount--;
14334
14335 if (_p.batchCount === 0) {
14336 // update style for dirty eles
14337 _p.batchStyleEles.updateStyle();
14338
14339 var renderer = this.renderer(); // notify the renderer of queued eles and event types
14340
14341 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14342 var eles = _p.batchNotifications[eventName];
14343
14344 if (eles.empty()) {
14345 renderer.notify(eventName);
14346 } else {
14347 renderer.notify(eventName, eles);
14348 }
14349 });
14350 }
14351
14352 return this;
14353 },
14354 batch: function batch(callback) {
14355 this.startBatch();
14356 callback();
14357 this.endBatch();
14358 return this;
14359 },
14360 // for backwards compatibility
14361 batchData: function batchData(map) {
14362 var cy = this;
14363 return this.batch(function () {
14364 var ids = Object.keys(map);
14365
14366 for (var i = 0; i < ids.length; i++) {
14367 var id = ids[i];
14368 var data = map[id];
14369 var ele = cy.getElementById(id);
14370 ele.data(data);
14371 }
14372 });
14373 }
14374};
14375
14376var rendererDefaults = defaults({
14377 hideEdgesOnViewport: false,
14378 textureOnViewport: false,
14379 motionBlur: false,
14380 motionBlurOpacity: 0.05,
14381 pixelRatio: undefined,
14382 desktopTapThreshold: 4,
14383 touchTapThreshold: 8,
14384 wheelSensitivity: 1,
14385 debug: false,
14386 showFps: false
14387});
14388var corefn$5 = {
14389 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14390 var r = this._private.renderer;
14391 r.renderTo(context, zoom, pan, pxRatio);
14392 return this;
14393 },
14394 renderer: function renderer() {
14395 return this._private.renderer;
14396 },
14397 forceRender: function forceRender() {
14398 this.notify('draw');
14399 return this;
14400 },
14401 resize: function resize() {
14402 this.invalidateSize();
14403 this.emitAndNotify('resize');
14404 return this;
14405 },
14406 initRenderer: function initRenderer(options) {
14407 var cy = this;
14408 var RendererProto = cy.extension('renderer', options.name);
14409
14410 if (RendererProto == null) {
14411 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14412 return;
14413 }
14414
14415 if (options.wheelSensitivity !== undefined) {
14416 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.");
14417 }
14418
14419 var rOpts = rendererDefaults(options);
14420 rOpts.cy = cy;
14421 cy._private.renderer = new RendererProto(rOpts);
14422 this.notify('init');
14423 },
14424 destroyRenderer: function destroyRenderer() {
14425 var cy = this;
14426 cy.notify('destroy'); // destroy the renderer
14427
14428 var domEle = cy.container();
14429
14430 if (domEle) {
14431 domEle._cyreg = null;
14432
14433 while (domEle.childNodes.length > 0) {
14434 domEle.removeChild(domEle.childNodes[0]);
14435 }
14436 }
14437
14438 cy._private.renderer = null; // to be extra safe, remove the ref
14439
14440 cy.mutableElements().forEach(function (ele) {
14441 var _p = ele._private;
14442 _p.rscratch = {};
14443 _p.rstyle = {};
14444 _p.animation.current = [];
14445 _p.animation.queue = [];
14446 });
14447 },
14448 onRender: function onRender(fn) {
14449 return this.on('render', fn);
14450 },
14451 offRender: function offRender(fn) {
14452 return this.off('render', fn);
14453 }
14454};
14455corefn$5.invalidateDimensions = corefn$5.resize;
14456
14457var corefn$6 = {
14458 // get a collection
14459 // - empty collection on no args
14460 // - collection of elements in the graph on selector arg
14461 // - guarantee a returned collection when elements or collection specified
14462 collection: function collection(eles, opts) {
14463 if (string(eles)) {
14464 return this.$(eles);
14465 } else if (elementOrCollection(eles)) {
14466 return eles.collection();
14467 } else if (array(eles)) {
14468 return new Collection(this, eles, opts);
14469 }
14470
14471 return new Collection(this);
14472 },
14473 nodes: function nodes(selector) {
14474 var nodes = this.$(function (ele) {
14475 return ele.isNode();
14476 });
14477
14478 if (selector) {
14479 return nodes.filter(selector);
14480 }
14481
14482 return nodes;
14483 },
14484 edges: function edges(selector) {
14485 var edges = this.$(function (ele) {
14486 return ele.isEdge();
14487 });
14488
14489 if (selector) {
14490 return edges.filter(selector);
14491 }
14492
14493 return edges;
14494 },
14495 // search the graph like jQuery
14496 $: function $(selector) {
14497 var eles = this._private.elements;
14498
14499 if (selector) {
14500 return eles.filter(selector);
14501 } else {
14502 return eles.spawnSelf();
14503 }
14504 },
14505 mutableElements: function mutableElements() {
14506 return this._private.elements;
14507 }
14508}; // aliases
14509
14510corefn$6.elements = corefn$6.filter = corefn$6.$;
14511
14512var styfn = {}; // keys for style blocks, e.g. ttfftt
14513
14514var TRUE = 't';
14515var FALSE = 'f'; // (potentially expensive calculation)
14516// apply the style to the element based on
14517// - its bypass
14518// - what selectors match it
14519
14520styfn.apply = function (eles) {
14521 var self = this;
14522 var _p = self._private;
14523 var cy = _p.cy;
14524 var updatedEles = cy.collection();
14525
14526 if (_p.newStyle) {
14527 // clear style caches
14528 _p.contextStyles = {};
14529 _p.propDiffs = {};
14530 self.cleanElements(eles, true);
14531 }
14532
14533 for (var ie = 0; ie < eles.length; ie++) {
14534 var ele = eles[ie];
14535 var cxtMeta = self.getContextMeta(ele);
14536
14537 if (cxtMeta.empty) {
14538 continue;
14539 }
14540
14541 var cxtStyle = self.getContextStyle(cxtMeta);
14542 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
14543
14544 if (!_p.newStyle) {
14545 self.updateTransitions(ele, app.diffProps);
14546 }
14547
14548 var hintsDiff = self.updateStyleHints(ele);
14549
14550 if (hintsDiff) {
14551 updatedEles.merge(ele);
14552 }
14553 } // for elements
14554
14555
14556 _p.newStyle = false;
14557 return updatedEles;
14558};
14559
14560styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
14561 var self = this;
14562 var cache = self._private.propDiffs = self._private.propDiffs || {};
14563 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
14564 var cachedVal = cache[dualCxtKey];
14565
14566 if (cachedVal) {
14567 return cachedVal;
14568 }
14569
14570 var diffProps = [];
14571 var addedProp = {};
14572
14573 for (var i = 0; i < self.length; i++) {
14574 var cxt = self[i];
14575 var oldHasCxt = oldCxtKey[i] === TRUE;
14576 var newHasCxt = newCxtKey[i] === TRUE;
14577 var cxtHasDiffed = oldHasCxt !== newHasCxt;
14578 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
14579
14580 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
14581 var props = void 0;
14582
14583 if (cxtHasDiffed && cxtHasMappedProps) {
14584 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
14585 } else if (cxtHasDiffed) {
14586 props = cxt.properties; // need to check them all
14587 } else if (cxtHasMappedProps) {
14588 props = cxt.mappedProperties; // only need to check mapped
14589 }
14590
14591 for (var j = 0; j < props.length; j++) {
14592 var prop = props[j];
14593 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
14594 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
14595 // is cached)
14596
14597 var laterCxtOverrides = false;
14598
14599 for (var k = i + 1; k < self.length; k++) {
14600 var laterCxt = self[k];
14601 var hasLaterCxt = newCxtKey[k] === TRUE;
14602
14603 if (!hasLaterCxt) {
14604 continue;
14605 } // can't override unless the context is active
14606
14607
14608 laterCxtOverrides = laterCxt.properties[prop.name] != null;
14609
14610 if (laterCxtOverrides) {
14611 break;
14612 } // exit early as long as one later context overrides
14613
14614 }
14615
14616 if (!addedProp[name] && !laterCxtOverrides) {
14617 addedProp[name] = true;
14618 diffProps.push(name);
14619 }
14620 } // for props
14621
14622 } // if
14623
14624 } // for contexts
14625
14626
14627 cache[dualCxtKey] = diffProps;
14628 return diffProps;
14629};
14630
14631styfn.getContextMeta = function (ele) {
14632 var self = this;
14633 var cxtKey = '';
14634 var diffProps;
14635 var prevKey = ele._private.styleCxtKey || '';
14636
14637 if (self._private.newStyle) {
14638 prevKey = ''; // since we need to apply all style if a fresh stylesheet
14639 } // get the cxt key
14640
14641
14642 for (var i = 0; i < self.length; i++) {
14643 var context = self[i];
14644 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
14645
14646 if (contextSelectorMatches) {
14647 cxtKey += TRUE;
14648 } else {
14649 cxtKey += FALSE;
14650 }
14651 } // for context
14652
14653
14654 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
14655 ele._private.styleCxtKey = cxtKey;
14656 return {
14657 key: cxtKey,
14658 diffPropNames: diffProps,
14659 empty: diffProps.length === 0
14660 };
14661}; // gets a computed ele style object based on matched contexts
14662
14663
14664styfn.getContextStyle = function (cxtMeta) {
14665 var cxtKey = cxtMeta.key;
14666 var self = this;
14667 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
14668
14669 if (cxtStyles[cxtKey]) {
14670 return cxtStyles[cxtKey];
14671 }
14672
14673 var style = {
14674 _private: {
14675 key: cxtKey
14676 }
14677 };
14678
14679 for (var i = 0; i < self.length; i++) {
14680 var cxt = self[i];
14681 var hasCxt = cxtKey[i] === TRUE;
14682
14683 if (!hasCxt) {
14684 continue;
14685 }
14686
14687 for (var j = 0; j < cxt.properties.length; j++) {
14688 var prop = cxt.properties[j];
14689 style[prop.name] = prop;
14690 }
14691 }
14692
14693 cxtStyles[cxtKey] = style;
14694 return style;
14695};
14696
14697styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
14698 var self = this;
14699 var diffProps = cxtMeta.diffPropNames;
14700 var retDiffProps = {};
14701 var types = self.types;
14702
14703 for (var i = 0; i < diffProps.length; i++) {
14704 var diffPropName = diffProps[i];
14705 var cxtProp = cxtStyle[diffPropName];
14706 var eleProp = ele.pstyle(diffPropName);
14707
14708 if (!cxtProp) {
14709 // no context prop means delete
14710 if (!eleProp) {
14711 continue; // no existing prop means nothing needs to be removed
14712 // nb affects initial application on mapped values like control-point-distances
14713 } else if (eleProp.bypass) {
14714 cxtProp = {
14715 name: diffPropName,
14716 deleteBypassed: true
14717 };
14718 } else {
14719 cxtProp = {
14720 name: diffPropName,
14721 "delete": true
14722 };
14723 }
14724 } // save cycles when the context prop doesn't need to be applied
14725
14726
14727 if (eleProp === cxtProp) {
14728 continue;
14729 } // save cycles when a mapped context prop doesn't need to be applied
14730
14731
14732 if (cxtProp.mapped === types.fn // context prop is function mapper
14733 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
14734 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
14735 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
14736 ) {
14737 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
14738 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
14739
14740 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
14741
14742 if (fnValue === mapping.prevFnValue) {
14743 continue;
14744 }
14745 }
14746
14747 var retDiffProp = retDiffProps[diffPropName] = {
14748 prev: eleProp
14749 };
14750 self.applyParsedProperty(ele, cxtProp);
14751 retDiffProp.next = ele.pstyle(diffPropName);
14752
14753 if (retDiffProp.next && retDiffProp.next.bypass) {
14754 retDiffProp.next = retDiffProp.next.bypassed;
14755 }
14756 }
14757
14758 return {
14759 diffProps: retDiffProps
14760 };
14761};
14762
14763styfn.updateStyleHints = function (ele) {
14764 var _p = ele._private;
14765 var self = this;
14766 var propNames = self.propertyGroupNames;
14767 var propGrKeys = self.propertyGroupKeys;
14768
14769 var propHash = function propHash(ele, propNames, seedKey) {
14770 return self.getPropertiesHash(ele, propNames, seedKey);
14771 };
14772
14773 var oldStyleKey = _p.styleKey;
14774
14775 if (ele.removed()) {
14776 return false;
14777 }
14778
14779 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
14780 // but lazily -- only use non-default prop values to reduce the number of hashes
14781 //
14782
14783 var overriddenStyles = ele._private.style;
14784 propNames = Object.keys(overriddenStyles);
14785
14786 for (var i = 0; i < propGrKeys.length; i++) {
14787 var grKey = propGrKeys[i];
14788 _p.styleKeys[grKey] = 0;
14789 }
14790
14791 var updateGrKey = function updateGrKey(val, grKey) {
14792 return _p.styleKeys[grKey] = hashInt(val, _p.styleKeys[grKey]);
14793 };
14794
14795 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
14796 for (var j = 0; j < strVal.length; j++) {
14797 updateGrKey(strVal.charCodeAt(j), grKey);
14798 }
14799 }; // - hashing works on 32 bit ints b/c we use bitwise ops
14800 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
14801 // - raise up small numbers so more significant digits are seen by hashing
14802 // - make small numbers larger than a normal value to avoid collisions
14803 // - works in practice and it's relatively cheap
14804
14805
14806 var N = 2000000000;
14807
14808 var cleanNum = function cleanNum(val) {
14809 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
14810 };
14811
14812 for (var _i = 0; _i < propNames.length; _i++) {
14813 var name = propNames[_i];
14814 var parsedProp = overriddenStyles[name];
14815
14816 if (parsedProp == null) {
14817 continue;
14818 }
14819
14820 var propInfo = this.properties[name];
14821 var type = propInfo.type;
14822 var _grKey = propInfo.groupKey;
14823 var normalizedNumberVal = void 0;
14824
14825 if (propInfo.hashOverride != null) {
14826 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
14827 } else if (parsedProp.pfValue != null) {
14828 normalizedNumberVal = parsedProp.pfValue;
14829 } // might not be a number if it allows enums
14830
14831
14832 var numberVal = propInfo.enums == null ? parsedProp.value : null;
14833 var haveNormNum = normalizedNumberVal != null;
14834 var haveUnitedNum = numberVal != null;
14835 var haveNum = haveNormNum || haveUnitedNum;
14836 var units = parsedProp.units; // numbers are cheaper to hash than strings
14837 // 1 hash op vs n hash ops (for length n string)
14838
14839 if (type.number && haveNum) {
14840 var v = haveNormNum ? normalizedNumberVal : numberVal;
14841
14842 if (type.multiple) {
14843 for (var _i2 = 0; _i2 < v.length; _i2++) {
14844 updateGrKey(cleanNum(v[_i2]), _grKey);
14845 }
14846 } else {
14847 updateGrKey(cleanNum(v), _grKey);
14848 }
14849
14850 if (!haveNormNum && units != null) {
14851 updateGrKeyWStr(units, _grKey);
14852 }
14853 } else {
14854 updateGrKeyWStr(parsedProp.strValue, _grKey);
14855 }
14856 } // overall style key
14857 //
14858
14859
14860 var hash = 0;
14861
14862 for (var _i3 = 0; _i3 < propGrKeys.length; _i3++) {
14863 var _grKey2 = propGrKeys[_i3];
14864 var grHash = _p.styleKeys[_grKey2];
14865 hash = hashInt(grHash, hash);
14866 }
14867
14868 _p.styleKey = hash; // label dims
14869 //
14870
14871 var labelDimsKey = _p.labelDimsKey = _p.styleKeys.labelDimensions;
14872 _p.labelKey = propHash(ele, ['label'], labelDimsKey);
14873 _p.labelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.labelKey);
14874
14875 if (!isNode) {
14876 _p.sourceLabelKey = propHash(ele, ['source-label'], labelDimsKey);
14877 _p.sourceLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.sourceLabelKey);
14878 _p.targetLabelKey = propHash(ele, ['target-label'], labelDimsKey);
14879 _p.targetLabelStyleKey = hashInt(_p.styleKeys.commonLabel, _p.targetLabelKey);
14880 } // node
14881 //
14882
14883
14884 if (isNode) {
14885 var _p$styleKeys = _p.styleKeys,
14886 nodeBody = _p$styleKeys.nodeBody,
14887 nodeBorder = _p$styleKeys.nodeBorder,
14888 backgroundImage = _p$styleKeys.backgroundImage,
14889 compound = _p$styleKeys.compound,
14890 pie = _p$styleKeys.pie;
14891 _p.nodeKey = hashIntsArray([nodeBorder, backgroundImage, compound, pie], nodeBody);
14892 _p.hasPie = pie != 0;
14893 }
14894
14895 return oldStyleKey !== _p.styleKey;
14896};
14897
14898styfn.clearStyleHints = function (ele) {
14899 var _p = ele._private;
14900 _p.styleKeys = {};
14901 _p.styleKey = null;
14902 _p.labelKey = null;
14903 _p.labelStyleKey = null;
14904 _p.sourceLabelKey = null;
14905 _p.sourceLabelStyleKey = null;
14906 _p.targetLabelKey = null;
14907 _p.targetLabelStyleKey = null;
14908 _p.nodeKey = null;
14909 _p.hasPie = null;
14910}; // apply a property to the style (for internal use)
14911// returns whether application was successful
14912//
14913// now, this function flattens the property, and here's how:
14914//
14915// for parsedProp:{ bypass: true, deleteBypass: true }
14916// no property is generated, instead the bypass property in the
14917// element's style is replaced by what's pointed to by the `bypassed`
14918// field in the bypass property (i.e. restoring the property the
14919// bypass was overriding)
14920//
14921// for parsedProp:{ mapped: truthy }
14922// the generated flattenedProp:{ mapping: prop }
14923//
14924// for parsedProp:{ bypass: true }
14925// the generated flattenedProp:{ bypassed: parsedProp }
14926
14927
14928styfn.applyParsedProperty = function (ele, parsedProp) {
14929 var self = this;
14930 var prop = parsedProp;
14931 var style = ele._private.style;
14932 var flatProp;
14933 var types = self.types;
14934 var type = self.properties[prop.name].type;
14935 var propIsBypass = prop.bypass;
14936 var origProp = style[prop.name];
14937 var origPropIsBypass = origProp && origProp.bypass;
14938 var _p = ele._private;
14939 var flatPropMapping = 'mapping';
14940
14941 var getVal = function getVal(p) {
14942 if (p == null) {
14943 return null;
14944 } else if (p.pfValue != null) {
14945 return p.pfValue;
14946 } else {
14947 return p.value;
14948 }
14949 };
14950
14951 var checkTriggers = function checkTriggers() {
14952 var fromVal = getVal(origProp);
14953 var toVal = getVal(prop);
14954 self.checkTriggers(ele, prop.name, fromVal, toVal);
14955 }; // edge sanity checks to prevent the client from making serious mistakes
14956
14957
14958 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
14959 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
14960 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
14961 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
14962 }
14963
14964 if (prop["delete"]) {
14965 // delete the property and use the default value on falsey value
14966 style[prop.name] = undefined;
14967 checkTriggers();
14968 return true;
14969 }
14970
14971 if (prop.deleteBypassed) {
14972 // delete the property that the
14973 if (!origProp) {
14974 checkTriggers();
14975 return true; // can't delete if no prop
14976 } else if (origProp.bypass) {
14977 // delete bypassed
14978 origProp.bypassed = undefined;
14979 checkTriggers();
14980 return true;
14981 } else {
14982 return false; // we're unsuccessful deleting the bypassed
14983 }
14984 } // check if we need to delete the current bypass
14985
14986
14987 if (prop.deleteBypass) {
14988 // then this property is just here to indicate we need to delete
14989 if (!origProp) {
14990 checkTriggers();
14991 return true; // property is already not defined
14992 } else if (origProp.bypass) {
14993 // then replace the bypass property with the original
14994 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
14995 style[prop.name] = origProp.bypassed;
14996 checkTriggers();
14997 return true;
14998 } else {
14999 return false; // we're unsuccessful deleting the bypass
15000 }
15001 }
15002
15003 var printMappingErr = function printMappingErr() {
15004 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');
15005 }; // put the property in the style objects
15006
15007
15008 switch (prop.mapped) {
15009 // flatten the property if mapped
15010 case types.mapData:
15011 {
15012 // flatten the field (e.g. data.foo.bar)
15013 var fields = prop.field.split('.');
15014 var fieldVal = _p.data;
15015
15016 for (var i = 0; i < fields.length && fieldVal; i++) {
15017 var field = fields[i];
15018 fieldVal = fieldVal[field];
15019 }
15020
15021 if (fieldVal == null) {
15022 printMappingErr();
15023 return false;
15024 }
15025
15026 var percent;
15027
15028 if (!number(fieldVal)) {
15029 // then don't apply and fall back on the existing style
15030 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
15031 return false;
15032 } else {
15033 var fieldWidth = prop.fieldMax - prop.fieldMin;
15034
15035 if (fieldWidth === 0) {
15036 // safety check -- not strictly necessary as no props of zero range should be passed here
15037 percent = 0;
15038 } else {
15039 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15040 }
15041 } // make sure to bound percent value
15042
15043
15044 if (percent < 0) {
15045 percent = 0;
15046 } else if (percent > 1) {
15047 percent = 1;
15048 }
15049
15050 if (type.color) {
15051 var r1 = prop.valueMin[0];
15052 var r2 = prop.valueMax[0];
15053 var g1 = prop.valueMin[1];
15054 var g2 = prop.valueMax[1];
15055 var b1 = prop.valueMin[2];
15056 var b2 = prop.valueMax[2];
15057 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15058 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15059 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)];
15060 flatProp = {
15061 // colours are simple, so just create the flat property instead of expensive string parsing
15062 bypass: prop.bypass,
15063 // we're a bypass if the mapping property is a bypass
15064 name: prop.name,
15065 value: clr,
15066 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15067 };
15068 } else if (type.number) {
15069 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15070 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15071 } else {
15072 return false; // can only map to colours and numbers
15073 }
15074
15075 if (!flatProp) {
15076 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15077 printMappingErr();
15078 return false;
15079 }
15080
15081 flatProp.mapping = prop; // keep a reference to the mapping
15082
15083 prop = flatProp; // the flattened (mapped) property is the one we want
15084
15085 break;
15086 }
15087 // direct mapping
15088
15089 case types.data:
15090 {
15091 // flatten the field (e.g. data.foo.bar)
15092 var _fields = prop.field.split('.');
15093
15094 var _fieldVal = _p.data;
15095
15096 for (var _i4 = 0; _i4 < _fields.length && _fieldVal; _i4++) {
15097 var _field = _fields[_i4];
15098 _fieldVal = _fieldVal[_field];
15099 }
15100
15101 if (_fieldVal != null) {
15102 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15103 }
15104
15105 if (!flatProp) {
15106 // if we can't flatten the property, then don't apply and fall back on the existing style
15107 printMappingErr();
15108 return false;
15109 }
15110
15111 flatProp.mapping = prop; // keep a reference to the mapping
15112
15113 prop = flatProp; // the flattened (mapped) property is the one we want
15114
15115 break;
15116 }
15117
15118 case types.fn:
15119 {
15120 var fn = prop.value;
15121 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15122
15123 prop.prevFnValue = fnRetVal;
15124
15125 if (fnRetVal == null) {
15126 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15127 return false;
15128 }
15129
15130 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15131
15132 if (!flatProp) {
15133 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15134 return false;
15135 }
15136
15137 flatProp.mapping = copy(prop); // keep a reference to the mapping
15138
15139 prop = flatProp; // the flattened (mapped) property is the one we want
15140
15141 break;
15142 }
15143
15144 case undefined:
15145 break;
15146 // just set the property
15147
15148 default:
15149 return false;
15150 // not a valid mapping
15151 } // if the property is a bypass property, then link the resultant property to the original one
15152
15153
15154 if (propIsBypass) {
15155 if (origPropIsBypass) {
15156 // then this bypass overrides the existing one
15157 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15158 } else {
15159 // then link the orig prop to the new bypass
15160 prop.bypassed = origProp;
15161 }
15162
15163 style[prop.name] = prop; // and set
15164 } else {
15165 // prop is not bypass
15166 if (origPropIsBypass) {
15167 // then keep the orig prop (since it's a bypass) and link to the new prop
15168 origProp.bypassed = prop;
15169 } else {
15170 // then just replace the old prop with the new one
15171 style[prop.name] = prop;
15172 }
15173 }
15174
15175 checkTriggers();
15176 return true;
15177};
15178
15179styfn.cleanElements = function (eles, keepBypasses) {
15180 for (var i = 0; i < eles.length; i++) {
15181 var ele = eles[i];
15182 this.clearStyleHints(ele);
15183 ele.dirtyCompoundBoundsCache();
15184 ele.dirtyBoundingBoxCache();
15185
15186 if (!keepBypasses) {
15187 ele._private.style = {};
15188 } else {
15189 var style = ele._private.style;
15190 var propNames = Object.keys(style);
15191
15192 for (var j = 0; j < propNames.length; j++) {
15193 var propName = propNames[j];
15194 var eleProp = style[propName];
15195
15196 if (eleProp != null) {
15197 if (eleProp.bypass) {
15198 eleProp.bypassed = null;
15199 } else {
15200 style[propName] = null;
15201 }
15202 }
15203 }
15204 }
15205 }
15206}; // updates the visual style for all elements (useful for manual style modification after init)
15207
15208
15209styfn.update = function () {
15210 var cy = this._private.cy;
15211 var eles = cy.mutableElements();
15212 eles.updateStyle();
15213}; // diffProps : { name => { prev, next } }
15214
15215
15216styfn.updateTransitions = function (ele, diffProps) {
15217 var self = this;
15218 var _p = ele._private;
15219 var props = ele.pstyle('transition-property').value;
15220 var duration = ele.pstyle('transition-duration').pfValue;
15221 var delay = ele.pstyle('transition-delay').pfValue;
15222
15223 if (props.length > 0 && duration > 0) {
15224 var style = {}; // build up the style to animate towards
15225
15226 var anyPrev = false;
15227
15228 for (var i = 0; i < props.length; i++) {
15229 var prop = props[i];
15230 var styProp = ele.pstyle(prop);
15231 var diffProp = diffProps[prop];
15232
15233 if (!diffProp) {
15234 continue;
15235 }
15236
15237 var prevProp = diffProp.prev;
15238 var fromProp = prevProp;
15239 var toProp = diffProp.next != null ? diffProp.next : styProp;
15240 var diff = false;
15241 var initVal = void 0;
15242 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15243
15244 if (!fromProp) {
15245 continue;
15246 } // consider px values
15247
15248
15249 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
15250 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15251
15252 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
15253 } else if (number(fromProp.value) && number(toProp.value)) {
15254 diff = toProp.value - fromProp.value; // nonzero is truthy
15255
15256 initVal = fromProp.value + initDt * diff; // consider colour values
15257 } else if (array(fromProp.value) && array(toProp.value)) {
15258 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15259 initVal = fromProp.strValue;
15260 } // the previous value is good for an animation only if it's different
15261
15262
15263 if (diff) {
15264 style[prop] = toProp.strValue; // to val
15265
15266 this.applyBypass(ele, prop, initVal); // from val
15267
15268 anyPrev = true;
15269 }
15270 } // end if props allow ani
15271 // can't transition if there's nothing previous to transition from
15272
15273
15274 if (!anyPrev) {
15275 return;
15276 }
15277
15278 _p.transitioning = true;
15279 new Promise$1(function (resolve) {
15280 if (delay > 0) {
15281 ele.delayAnimation(delay).play().promise().then(resolve);
15282 } else {
15283 resolve();
15284 }
15285 }).then(function () {
15286 return ele.animation({
15287 style: style,
15288 duration: duration,
15289 easing: ele.pstyle('transition-timing-function').value,
15290 queue: false
15291 }).play().promise();
15292 }).then(function () {
15293 // if( !isBypass ){
15294 self.removeBypasses(ele, props);
15295 ele.emitAndNotify('style'); // }
15296
15297 _p.transitioning = false;
15298 });
15299 } else if (_p.transitioning) {
15300 this.removeBypasses(ele, props);
15301 ele.emitAndNotify('style');
15302 _p.transitioning = false;
15303 }
15304};
15305
15306styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15307 var prop = this.properties[name];
15308 var triggerCheck = getTrigger(prop);
15309
15310 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15311 onTrigger(prop);
15312 }
15313};
15314
15315styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15316 var _this = this;
15317
15318 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15319 return prop.triggersZOrder;
15320 }, function () {
15321 _this._private.cy.notify('zorder', ele);
15322 });
15323};
15324
15325styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15326 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15327 return prop.triggersBounds;
15328 }, function (prop) {
15329 ele.dirtyCompoundBoundsCache();
15330 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
15331 // then dirty the pll edge bb cache as well
15332
15333 if ( // only for beziers -- so performance of other edges isn't affected
15334 (ele.pstyle('curve-style').value === 'bezier' // already a bezier
15335 // was just now changed to or from a bezier:
15336 || name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier')) && prop.triggersBoundsOfParallelBeziers) {
15337 ele.parallelEdges().forEach(function (pllEdge) {
15338 if (pllEdge.isBundledBezier()) {
15339 pllEdge.dirtyBoundingBoxCache();
15340 }
15341 });
15342 }
15343 });
15344};
15345
15346styfn.checkTriggers = function (ele, name, fromValue, toValue) {
15347 ele.dirtyStyleCache();
15348 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15349 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15350};
15351
15352var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
15353// returns true iff application was successful for at least 1 specified property
15354
15355styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
15356 var self = this;
15357 var props = [];
15358 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
15359
15360 if (name === '*' || name === '**') {
15361 // apply to all property names
15362 if (value !== undefined) {
15363 for (var i = 0; i < self.properties.length; i++) {
15364 var prop = self.properties[i];
15365 var _name = prop.name;
15366 var parsedProp = this.parse(_name, value, true);
15367
15368 if (parsedProp) {
15369 props.push(parsedProp);
15370 }
15371 }
15372 }
15373 } else if (string(name)) {
15374 // then parse the single property
15375 var _parsedProp = this.parse(name, value, true);
15376
15377 if (_parsedProp) {
15378 props.push(_parsedProp);
15379 }
15380 } else if (plainObject(name)) {
15381 // then parse each property
15382 var specifiedProps = name;
15383 updateTransitions = value;
15384 var names = Object.keys(specifiedProps);
15385
15386 for (var _i = 0; _i < names.length; _i++) {
15387 var _name2 = names[_i];
15388 var _value = specifiedProps[_name2];
15389
15390 if (_value === undefined) {
15391 // try camel case name too
15392 _value = specifiedProps[dash2camel(_name2)];
15393 }
15394
15395 if (_value !== undefined) {
15396 var _parsedProp2 = this.parse(_name2, _value, true);
15397
15398 if (_parsedProp2) {
15399 props.push(_parsedProp2);
15400 }
15401 }
15402 }
15403 } else {
15404 // can't do anything without well defined properties
15405 return false;
15406 } // we've failed if there are no valid properties
15407
15408
15409 if (props.length === 0) {
15410 return false;
15411 } // now, apply the bypass properties on the elements
15412
15413
15414 var ret = false; // return true if at least one succesful bypass applied
15415
15416 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15417 // for each ele
15418 var ele = eles[_i2];
15419 var diffProps = {};
15420 var diffProp = void 0;
15421
15422 for (var j = 0; j < props.length; j++) {
15423 // for each prop
15424 var _prop = props[j];
15425
15426 if (updateTransitions) {
15427 var prevProp = ele.pstyle(_prop.name);
15428 diffProp = diffProps[_prop.name] = {
15429 prev: prevProp
15430 };
15431 }
15432
15433 ret = this.applyParsedProperty(ele, _prop) || ret;
15434
15435 if (updateTransitions) {
15436 diffProp.next = ele.pstyle(_prop.name);
15437 }
15438 } // for props
15439
15440
15441 if (ret) {
15442 this.updateStyleHints(ele);
15443 }
15444
15445 if (updateTransitions) {
15446 this.updateTransitions(ele, diffProps, isBypass);
15447 }
15448 } // for eles
15449
15450
15451 return ret;
15452}; // only useful in specific cases like animation
15453
15454
15455styfn$1.overrideBypass = function (eles, name, value) {
15456 name = camel2dash(name);
15457
15458 for (var i = 0; i < eles.length; i++) {
15459 var ele = eles[i];
15460 var prop = ele._private.style[name];
15461 var type = this.properties[name].type;
15462 var isColor = type.color;
15463 var isMulti = type.mutiple;
15464 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15465
15466 if (!prop || !prop.bypass) {
15467 // need a bypass if one doesn't exist
15468 this.applyBypass(ele, name, value);
15469 } else {
15470 prop.value = value;
15471
15472 if (prop.pfValue != null) {
15473 prop.pfValue = value;
15474 }
15475
15476 if (isColor) {
15477 prop.strValue = 'rgb(' + value.join(',') + ')';
15478 } else if (isMulti) {
15479 prop.strValue = value.join(' ');
15480 } else {
15481 prop.strValue = '' + value;
15482 }
15483
15484 this.updateStyleHints(ele);
15485 }
15486
15487 this.checkTriggers(ele, name, oldValue, value);
15488 }
15489};
15490
15491styfn$1.removeAllBypasses = function (eles, updateTransitions) {
15492 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15493};
15494
15495styfn$1.removeBypasses = function (eles, props, updateTransitions) {
15496 var isBypass = true;
15497
15498 for (var j = 0; j < eles.length; j++) {
15499 var ele = eles[j];
15500 var diffProps = {};
15501
15502 for (var i = 0; i < props.length; i++) {
15503 var name = props[i];
15504 var prop = this.properties[name];
15505 var prevProp = ele.pstyle(prop.name);
15506
15507 if (!prevProp || !prevProp.bypass) {
15508 // if a bypass doesn't exist for the prop, nothing needs to be removed
15509 continue;
15510 }
15511
15512 var value = ''; // empty => remove bypass
15513
15514 var parsedProp = this.parse(name, value, true);
15515 var diffProp = diffProps[prop.name] = {
15516 prev: prevProp
15517 };
15518 this.applyParsedProperty(ele, parsedProp);
15519 diffProp.next = ele.pstyle(prop.name);
15520 } // for props
15521
15522
15523 this.updateStyleHints(ele);
15524
15525 if (updateTransitions) {
15526 this.updateTransitions(ele, diffProps, isBypass);
15527 }
15528 } // for eles
15529
15530};
15531
15532var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
15533
15534styfn$2.getEmSizeInPixels = function () {
15535 var px = this.containerCss('font-size');
15536
15537 if (px != null) {
15538 return parseFloat(px);
15539 } else {
15540 return 1; // for headless
15541 }
15542}; // gets css property from the core container
15543
15544
15545styfn$2.containerCss = function (propName) {
15546 var cy = this._private.cy;
15547 var domElement = cy.container();
15548
15549 if (window$1 && domElement && window$1.getComputedStyle) {
15550 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
15551 }
15552};
15553
15554var styfn$3 = {}; // gets the rendered style for an element
15555
15556styfn$3.getRenderedStyle = function (ele, prop) {
15557 if (prop) {
15558 return this.getStylePropertyValue(ele, prop, true);
15559 } else {
15560 return this.getRawStyle(ele, true);
15561 }
15562}; // gets the raw style for an element
15563
15564
15565styfn$3.getRawStyle = function (ele, isRenderedVal) {
15566 var self = this;
15567 ele = ele[0]; // insure it's an element
15568
15569 if (ele) {
15570 var rstyle = {};
15571
15572 for (var i = 0; i < self.properties.length; i++) {
15573 var prop = self.properties[i];
15574 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15575
15576 if (val != null) {
15577 rstyle[prop.name] = val;
15578 rstyle[dash2camel(prop.name)] = val;
15579 }
15580 }
15581
15582 return rstyle;
15583 }
15584};
15585
15586styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
15587 var pstyle = ele.pstyle(property)[subproperty][index];
15588 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15589};
15590
15591styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15592 var self = this;
15593 ele = ele[0]; // insure it's an element
15594
15595 if (ele) {
15596 var prop = self.properties[propName];
15597
15598 if (prop.alias) {
15599 prop = prop.pointsTo;
15600 }
15601
15602 var type = prop.type;
15603 var styleProp = ele.pstyle(prop.name);
15604
15605 if (styleProp) {
15606 var value = styleProp.value,
15607 units = styleProp.units,
15608 strValue = styleProp.strValue;
15609
15610 if (isRenderedVal && type.number && value != null && number(value)) {
15611 var zoom = ele.cy().zoom();
15612
15613 var getRenderedValue = function getRenderedValue(val) {
15614 return val * zoom;
15615 };
15616
15617 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15618 return getRenderedValue(val) + units;
15619 };
15620
15621 var isArrayValue = array(value);
15622 var haveUnits = isArrayValue ? units.every(function (u) {
15623 return u != null;
15624 }) : units != null;
15625
15626 if (haveUnits) {
15627 if (isArrayValue) {
15628 return value.map(function (v, i) {
15629 return getValueStringWithUnits(v, units[i]);
15630 }).join(' ');
15631 } else {
15632 return getValueStringWithUnits(value, units);
15633 }
15634 } else {
15635 if (isArrayValue) {
15636 return value.map(function (v) {
15637 return string(v) ? v : '' + getRenderedValue(v);
15638 }).join(' ');
15639 } else {
15640 return '' + getRenderedValue(value);
15641 }
15642 }
15643 } else if (strValue != null) {
15644 return strValue;
15645 }
15646 }
15647
15648 return null;
15649 }
15650};
15651
15652styfn$3.getAnimationStartStyle = function (ele, aniProps) {
15653 var rstyle = {};
15654
15655 for (var i = 0; i < aniProps.length; i++) {
15656 var aniProp = aniProps[i];
15657 var name = aniProp.name;
15658 var styleProp = ele.pstyle(name);
15659
15660 if (styleProp !== undefined) {
15661 // then make a prop of it
15662 if (plainObject(styleProp)) {
15663 styleProp = this.parse(name, styleProp.strValue);
15664 } else {
15665 styleProp = this.parse(name, styleProp);
15666 }
15667 }
15668
15669 if (styleProp) {
15670 rstyle[name] = styleProp;
15671 }
15672 }
15673
15674 return rstyle;
15675};
15676
15677styfn$3.getPropsList = function (propsObj) {
15678 var self = this;
15679 var rstyle = [];
15680 var style = propsObj;
15681 var props = self.properties;
15682
15683 if (style) {
15684 var names = Object.keys(style);
15685
15686 for (var i = 0; i < names.length; i++) {
15687 var name = names[i];
15688 var val = style[name];
15689 var prop = props[name] || props[camel2dash(name)];
15690 var styleProp = this.parse(prop.name, val);
15691
15692 if (styleProp) {
15693 rstyle.push(styleProp);
15694 }
15695 }
15696 }
15697
15698 return rstyle;
15699};
15700
15701styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
15702 var hash = seed;
15703 var name, val, strVal, chVal;
15704 var i, j;
15705
15706 for (i = 0; i < propNames.length; i++) {
15707 name = propNames[i];
15708 val = ele.pstyle(name, false);
15709
15710 if (val == null) {
15711 continue;
15712 } else if (val.pfValue != null) {
15713 hash = hashInt(chVal, hash);
15714 } else {
15715 strVal = val.strValue;
15716
15717 for (j = 0; j < strVal.length; j++) {
15718 chVal = strVal.charCodeAt(j);
15719 hash = hashInt(chVal, hash);
15720 }
15721 }
15722 }
15723
15724 return hash;
15725};
15726
15727styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
15728
15729var styfn$4 = {};
15730
15731styfn$4.appendFromJson = function (json) {
15732 var style = this;
15733
15734 for (var i = 0; i < json.length; i++) {
15735 var context = json[i];
15736 var selector = context.selector;
15737 var props = context.style || context.css;
15738 var names = Object.keys(props);
15739 style.selector(selector); // apply selector
15740
15741 for (var j = 0; j < names.length; j++) {
15742 var name = names[j];
15743 var value = props[name];
15744 style.css(name, value); // apply property
15745 }
15746 }
15747
15748 return style;
15749}; // accessible cy.style() function
15750
15751
15752styfn$4.fromJson = function (json) {
15753 var style = this;
15754 style.resetToDefault();
15755 style.appendFromJson(json);
15756 return style;
15757}; // get json from cy.style() api
15758
15759
15760styfn$4.json = function () {
15761 var json = [];
15762
15763 for (var i = this.defaultLength; i < this.length; i++) {
15764 var cxt = this[i];
15765 var selector = cxt.selector;
15766 var props = cxt.properties;
15767 var css = {};
15768
15769 for (var j = 0; j < props.length; j++) {
15770 var prop = props[j];
15771 css[prop.name] = prop.strValue;
15772 }
15773
15774 json.push({
15775 selector: !selector ? 'core' : selector.toString(),
15776 style: css
15777 });
15778 }
15779
15780 return json;
15781};
15782
15783var styfn$5 = {};
15784
15785styfn$5.appendFromString = function (string) {
15786 var self = this;
15787 var style = this;
15788 var remaining = '' + string;
15789 var selAndBlockStr;
15790 var blockRem;
15791 var propAndValStr; // remove comments from the style string
15792
15793 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
15794
15795 function removeSelAndBlockFromRemaining() {
15796 // remove the parsed selector and block from the remaining text to parse
15797 if (remaining.length > selAndBlockStr.length) {
15798 remaining = remaining.substr(selAndBlockStr.length);
15799 } else {
15800 remaining = '';
15801 }
15802 }
15803
15804 function removePropAndValFromRem() {
15805 // remove the parsed property and value from the remaining block text to parse
15806 if (blockRem.length > propAndValStr.length) {
15807 blockRem = blockRem.substr(propAndValStr.length);
15808 } else {
15809 blockRem = '';
15810 }
15811 }
15812
15813 for (;;) {
15814 var nothingLeftToParse = remaining.match(/^\s*$/);
15815
15816 if (nothingLeftToParse) {
15817 break;
15818 }
15819
15820 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
15821
15822 if (!selAndBlock) {
15823 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
15824 break;
15825 }
15826
15827 selAndBlockStr = selAndBlock[0]; // parse the selector
15828
15829 var selectorStr = selAndBlock[1];
15830
15831 if (selectorStr !== 'core') {
15832 var selector = new Selector(selectorStr);
15833
15834 if (selector.invalid) {
15835 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
15836
15837 removeSelAndBlockFromRemaining();
15838 continue;
15839 }
15840 } // parse the block of properties and values
15841
15842
15843 var blockStr = selAndBlock[2];
15844 var invalidBlock = false;
15845 blockRem = blockStr;
15846 var props = [];
15847
15848 for (;;) {
15849 var _nothingLeftToParse = blockRem.match(/^\s*$/);
15850
15851 if (_nothingLeftToParse) {
15852 break;
15853 }
15854
15855 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
15856
15857 if (!propAndVal) {
15858 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
15859 invalidBlock = true;
15860 break;
15861 }
15862
15863 propAndValStr = propAndVal[0];
15864 var propStr = propAndVal[1];
15865 var valStr = propAndVal[2];
15866 var prop = self.properties[propStr];
15867
15868 if (!prop) {
15869 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
15870
15871 removePropAndValFromRem();
15872 continue;
15873 }
15874
15875 var parsedProp = style.parse(propStr, valStr);
15876
15877 if (!parsedProp) {
15878 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
15879
15880 removePropAndValFromRem();
15881 continue;
15882 }
15883
15884 props.push({
15885 name: propStr,
15886 val: valStr
15887 });
15888 removePropAndValFromRem();
15889 }
15890
15891 if (invalidBlock) {
15892 removeSelAndBlockFromRemaining();
15893 break;
15894 } // put the parsed block in the style
15895
15896
15897 style.selector(selectorStr);
15898
15899 for (var i = 0; i < props.length; i++) {
15900 var _prop = props[i];
15901 style.css(_prop.name, _prop.val);
15902 }
15903
15904 removeSelAndBlockFromRemaining();
15905 }
15906
15907 return style;
15908};
15909
15910styfn$5.fromString = function (string) {
15911 var style = this;
15912 style.resetToDefault();
15913 style.appendFromString(string);
15914 return style;
15915};
15916
15917var styfn$6 = {};
15918
15919(function () {
15920 var number = number$1;
15921 var rgba = rgbaNoBackRefs;
15922 var hsla = hslaNoBackRefs;
15923 var hex3$1 = hex3;
15924 var hex6$1 = hex6;
15925
15926 var data = function data(prefix) {
15927 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
15928 };
15929
15930 var mapData = function mapData(prefix) {
15931 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
15932 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
15933 };
15934
15935 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
15936
15937 styfn$6.types = {
15938 time: {
15939 number: true,
15940 min: 0,
15941 units: 's|ms',
15942 implicitUnits: 'ms'
15943 },
15944 percent: {
15945 number: true,
15946 min: 0,
15947 max: 100,
15948 units: '%',
15949 implicitUnits: '%'
15950 },
15951 percentages: {
15952 number: true,
15953 min: 0,
15954 max: 100,
15955 units: '%',
15956 implicitUnits: '%',
15957 multiple: true
15958 },
15959 zeroOneNumber: {
15960 number: true,
15961 min: 0,
15962 max: 1,
15963 unitless: true
15964 },
15965 zeroOneNumbers: {
15966 number: true,
15967 min: 0,
15968 max: 1,
15969 unitless: true,
15970 multiple: true
15971 },
15972 nOneOneNumber: {
15973 number: true,
15974 min: -1,
15975 max: 1,
15976 unitless: true
15977 },
15978 nonNegativeInt: {
15979 number: true,
15980 min: 0,
15981 integer: true,
15982 unitless: true
15983 },
15984 position: {
15985 enums: ['parent', 'origin']
15986 },
15987 nodeSize: {
15988 number: true,
15989 min: 0,
15990 enums: ['label']
15991 },
15992 number: {
15993 number: true,
15994 unitless: true
15995 },
15996 numbers: {
15997 number: true,
15998 unitless: true,
15999 multiple: true
16000 },
16001 positiveNumber: {
16002 number: true,
16003 unitless: true,
16004 min: 0,
16005 strictMin: true
16006 },
16007 size: {
16008 number: true,
16009 min: 0
16010 },
16011 bidirectionalSize: {
16012 number: true
16013 },
16014 // allows negative
16015 bidirectionalSizeMaybePercent: {
16016 number: true,
16017 allowPercent: true
16018 },
16019 // allows negative
16020 bidirectionalSizes: {
16021 number: true,
16022 multiple: true
16023 },
16024 // allows negative
16025 sizeMaybePercent: {
16026 number: true,
16027 min: 0,
16028 allowPercent: true
16029 },
16030 axisDirection: {
16031 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
16032 },
16033 paddingRelativeTo: {
16034 enums: ['width', 'height', 'average', 'min', 'max']
16035 },
16036 bgWH: {
16037 number: true,
16038 min: 0,
16039 allowPercent: true,
16040 enums: ['auto'],
16041 multiple: true
16042 },
16043 bgPos: {
16044 number: true,
16045 allowPercent: true,
16046 multiple: true
16047 },
16048 bgRelativeTo: {
16049 enums: ['inner', 'include-padding'],
16050 multiple: true
16051 },
16052 bgRepeat: {
16053 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16054 multiple: true
16055 },
16056 bgFit: {
16057 enums: ['none', 'contain', 'cover'],
16058 multiple: true
16059 },
16060 bgCrossOrigin: {
16061 enums: ['anonymous', 'use-credentials'],
16062 multiple: true
16063 },
16064 bgClip: {
16065 enums: ['none', 'node'],
16066 multiple: true
16067 },
16068 color: {
16069 color: true
16070 },
16071 colors: {
16072 color: true,
16073 multiple: true
16074 },
16075 fill: {
16076 enums: ['solid', 'linear-gradient', 'radial-gradient']
16077 },
16078 bool: {
16079 enums: ['yes', 'no']
16080 },
16081 lineStyle: {
16082 enums: ['solid', 'dotted', 'dashed']
16083 },
16084 lineCap: {
16085 enums: ['butt', 'round', 'square']
16086 },
16087 borderStyle: {
16088 enums: ['solid', 'dotted', 'dashed', 'double']
16089 },
16090 curveStyle: {
16091 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
16092 },
16093 fontFamily: {
16094 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16095 },
16096 fontStyle: {
16097 enums: ['italic', 'normal', 'oblique']
16098 },
16099 fontWeight: {
16100 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16101 },
16102 textDecoration: {
16103 enums: ['none', 'underline', 'overline', 'line-through']
16104 },
16105 textTransform: {
16106 enums: ['none', 'uppercase', 'lowercase']
16107 },
16108 textWrap: {
16109 enums: ['none', 'wrap', 'ellipsis']
16110 },
16111 textOverflowWrap: {
16112 enums: ['whitespace', 'anywhere']
16113 },
16114 textBackgroundShape: {
16115 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16116 },
16117 nodeShape: {
16118 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']
16119 },
16120 compoundIncludeLabels: {
16121 enums: ['include', 'exclude']
16122 },
16123 arrowShape: {
16124 enums: ['tee', 'triangle', 'triangle-tee', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16125 },
16126 arrowFill: {
16127 enums: ['filled', 'hollow']
16128 },
16129 display: {
16130 enums: ['element', 'none']
16131 },
16132 visibility: {
16133 enums: ['hidden', 'visible']
16134 },
16135 zCompoundDepth: {
16136 enums: ['bottom', 'orphan', 'auto', 'top']
16137 },
16138 zIndexCompare: {
16139 enums: ['auto', 'manual']
16140 },
16141 valign: {
16142 enums: ['top', 'center', 'bottom']
16143 },
16144 halign: {
16145 enums: ['left', 'center', 'right']
16146 },
16147 justification: {
16148 enums: ['left', 'center', 'right', 'auto']
16149 },
16150 text: {
16151 string: true
16152 },
16153 data: {
16154 mapping: true,
16155 regex: data('data')
16156 },
16157 layoutData: {
16158 mapping: true,
16159 regex: data('layoutData')
16160 },
16161 scratch: {
16162 mapping: true,
16163 regex: data('scratch')
16164 },
16165 mapData: {
16166 mapping: true,
16167 regex: mapData('mapData')
16168 },
16169 mapLayoutData: {
16170 mapping: true,
16171 regex: mapData('mapLayoutData')
16172 },
16173 mapScratch: {
16174 mapping: true,
16175 regex: mapData('mapScratch')
16176 },
16177 fn: {
16178 mapping: true,
16179 fn: true
16180 },
16181 url: {
16182 regexes: urlRegexes,
16183 singleRegexMatchValue: true
16184 },
16185 urls: {
16186 regexes: urlRegexes,
16187 singleRegexMatchValue: true,
16188 multiple: true
16189 },
16190 propList: {
16191 propList: true
16192 },
16193 angle: {
16194 number: true,
16195 units: 'deg|rad',
16196 implicitUnits: 'rad'
16197 },
16198 textRotation: {
16199 number: true,
16200 units: 'deg|rad',
16201 implicitUnits: 'rad',
16202 enums: ['none', 'autorotate']
16203 },
16204 polygonPointList: {
16205 number: true,
16206 multiple: true,
16207 evenMultiple: true,
16208 min: -1,
16209 max: 1,
16210 unitless: true
16211 },
16212 edgeDistances: {
16213 enums: ['intersection', 'node-position']
16214 },
16215 edgeEndpoint: {
16216 number: true,
16217 multiple: true,
16218 units: '%|px|em|deg|rad',
16219 implicitUnits: 'px',
16220 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16221 singleEnum: true,
16222 validate: function validate(valArr, unitsArr) {
16223 switch (valArr.length) {
16224 case 2:
16225 // can be % or px only
16226 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16227
16228 case 1:
16229 // can be enum, deg, or rad only
16230 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16231
16232 default:
16233 return false;
16234 }
16235 }
16236 },
16237 easing: {
16238 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
16239 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']
16240 },
16241 gradientDirection: {
16242 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']
16243 },
16244 boundsExpansion: {
16245 number: true,
16246 multiple: true,
16247 min: 0,
16248 validate: function validate(valArr) {
16249 var length = valArr.length;
16250 return length === 1 || length === 2 || length === 4;
16251 }
16252 }
16253 };
16254 var diff = {
16255 zeroNonZero: function zeroNonZero(val1, val2) {
16256 if ((val1 == null || val2 == null) && val1 !== val2) {
16257 return true; // null cases could represent any value
16258 }
16259
16260 if (val1 == 0 && val2 != 0) {
16261 return true;
16262 } else if (val1 != 0 && val2 == 0) {
16263 return true;
16264 } else {
16265 return false;
16266 }
16267 },
16268 any: function any(val1, val2) {
16269 return val1 != val2;
16270 }
16271 }; // define visual style properties
16272 //
16273 // - n.b. adding a new group of props may require updates to updateStyleHints()
16274 // - adding new props to an existing group gets handled automatically
16275
16276 var t = styfn$6.types;
16277 var mainLabel = [{
16278 name: 'label',
16279 type: t.text,
16280 triggersBounds: diff.any
16281 }, {
16282 name: 'text-rotation',
16283 type: t.textRotation,
16284 triggersBounds: diff.any
16285 }, {
16286 name: 'text-margin-x',
16287 type: t.bidirectionalSize,
16288 triggersBounds: diff.any
16289 }, {
16290 name: 'text-margin-y',
16291 type: t.bidirectionalSize,
16292 triggersBounds: diff.any
16293 }];
16294 var sourceLabel = [{
16295 name: 'source-label',
16296 type: t.text,
16297 triggersBounds: diff.any
16298 }, {
16299 name: 'source-text-rotation',
16300 type: t.textRotation,
16301 triggersBounds: diff.any
16302 }, {
16303 name: 'source-text-margin-x',
16304 type: t.bidirectionalSize,
16305 triggersBounds: diff.any
16306 }, {
16307 name: 'source-text-margin-y',
16308 type: t.bidirectionalSize,
16309 triggersBounds: diff.any
16310 }, {
16311 name: 'source-text-offset',
16312 type: t.size,
16313 triggersBounds: diff.any
16314 }];
16315 var targetLabel = [{
16316 name: 'target-label',
16317 type: t.text,
16318 triggersBounds: diff.any
16319 }, {
16320 name: 'target-text-rotation',
16321 type: t.textRotation,
16322 triggersBounds: diff.any
16323 }, {
16324 name: 'target-text-margin-x',
16325 type: t.bidirectionalSize,
16326 triggersBounds: diff.any
16327 }, {
16328 name: 'target-text-margin-y',
16329 type: t.bidirectionalSize,
16330 triggersBounds: diff.any
16331 }, {
16332 name: 'target-text-offset',
16333 type: t.size,
16334 triggersBounds: diff.any
16335 }];
16336 var labelDimensions = [{
16337 name: 'font-family',
16338 type: t.fontFamily,
16339 triggersBounds: diff.any
16340 }, {
16341 name: 'font-style',
16342 type: t.fontStyle,
16343 triggersBounds: diff.any
16344 }, {
16345 name: 'font-weight',
16346 type: t.fontWeight,
16347 triggersBounds: diff.any
16348 }, {
16349 name: 'font-size',
16350 type: t.size,
16351 triggersBounds: diff.any
16352 }, {
16353 name: 'text-transform',
16354 type: t.textTransform,
16355 triggersBounds: diff.any
16356 }, {
16357 name: 'text-wrap',
16358 type: t.textWrap,
16359 triggersBounds: diff.any
16360 }, {
16361 name: 'text-overflow-wrap',
16362 type: t.textOverflowWrap,
16363 triggersBounds: diff.any
16364 }, {
16365 name: 'text-max-width',
16366 type: t.size,
16367 triggersBounds: diff.any
16368 }, {
16369 name: 'text-outline-width',
16370 type: t.size,
16371 triggersBounds: diff.any
16372 }, {
16373 name: 'line-height',
16374 type: t.positiveNumber,
16375 triggersBounds: diff.any
16376 }];
16377 var commonLabel = [{
16378 name: 'text-valign',
16379 type: t.valign,
16380 triggersBounds: diff.any
16381 }, {
16382 name: 'text-halign',
16383 type: t.halign,
16384 triggersBounds: diff.any
16385 }, {
16386 name: 'color',
16387 type: t.color
16388 }, {
16389 name: 'text-outline-color',
16390 type: t.color
16391 }, {
16392 name: 'text-outline-opacity',
16393 type: t.zeroOneNumber
16394 }, {
16395 name: 'text-background-color',
16396 type: t.color
16397 }, {
16398 name: 'text-background-opacity',
16399 type: t.zeroOneNumber
16400 }, {
16401 name: 'text-background-padding',
16402 type: t.size,
16403 triggersBounds: diff.any
16404 }, {
16405 name: 'text-border-opacity',
16406 type: t.zeroOneNumber
16407 }, {
16408 name: 'text-border-color',
16409 type: t.color
16410 }, {
16411 name: 'text-border-width',
16412 type: t.size,
16413 triggersBounds: diff.any
16414 }, {
16415 name: 'text-border-style',
16416 type: t.borderStyle,
16417 triggersBounds: diff.any
16418 }, {
16419 name: 'text-background-shape',
16420 type: t.textBackgroundShape,
16421 triggersBounds: diff.any
16422 }, {
16423 name: 'text-justification',
16424 type: t.justification
16425 }];
16426 var behavior = [{
16427 name: 'events',
16428 type: t.bool
16429 }, {
16430 name: 'text-events',
16431 type: t.bool
16432 }];
16433 var visibility = [{
16434 name: 'display',
16435 type: t.display,
16436 triggersZOrder: diff.any,
16437 triggersBounds: diff.any,
16438 triggersBoundsOfParallelBeziers: true
16439 }, {
16440 name: 'visibility',
16441 type: t.visibility,
16442 triggersZOrder: diff.any
16443 }, {
16444 name: 'opacity',
16445 type: t.zeroOneNumber,
16446 triggersZOrder: diff.zeroNonZero
16447 }, {
16448 name: 'text-opacity',
16449 type: t.zeroOneNumber
16450 }, {
16451 name: 'min-zoomed-font-size',
16452 type: t.size
16453 }, {
16454 name: 'z-compound-depth',
16455 type: t.zCompoundDepth,
16456 triggersZOrder: diff.any
16457 }, {
16458 name: 'z-index-compare',
16459 type: t.zIndexCompare,
16460 triggersZOrder: diff.any
16461 }, {
16462 name: 'z-index',
16463 type: t.nonNegativeInt,
16464 triggersZOrder: diff.any
16465 }];
16466 var overlay = [{
16467 name: 'overlay-padding',
16468 type: t.size,
16469 triggersBounds: diff.any
16470 }, {
16471 name: 'overlay-color',
16472 type: t.color
16473 }, {
16474 name: 'overlay-opacity',
16475 type: t.zeroOneNumber,
16476 triggersBounds: diff.zeroNonZero
16477 }];
16478 var transition = [{
16479 name: 'transition-property',
16480 type: t.propList
16481 }, {
16482 name: 'transition-duration',
16483 type: t.time
16484 }, {
16485 name: 'transition-delay',
16486 type: t.time
16487 }, {
16488 name: 'transition-timing-function',
16489 type: t.easing
16490 }];
16491
16492 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16493 if (parsedProp.value === 'label') {
16494 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16495 } else {
16496 return parsedProp.pfValue;
16497 }
16498 };
16499
16500 var nodeBody = [{
16501 name: 'height',
16502 type: t.nodeSize,
16503 triggersBounds: diff.any,
16504 hashOverride: nodeSizeHashOverride
16505 }, {
16506 name: 'width',
16507 type: t.nodeSize,
16508 triggersBounds: diff.any,
16509 hashOverride: nodeSizeHashOverride
16510 }, {
16511 name: 'shape',
16512 type: t.nodeShape,
16513 triggersBounds: diff.any
16514 }, {
16515 name: 'shape-polygon-points',
16516 type: t.polygonPointList,
16517 triggersBounds: diff.any
16518 }, {
16519 name: 'background-color',
16520 type: t.color
16521 }, {
16522 name: 'background-fill',
16523 type: t.fill
16524 }, {
16525 name: 'background-opacity',
16526 type: t.zeroOneNumber
16527 }, {
16528 name: 'background-blacken',
16529 type: t.nOneOneNumber
16530 }, {
16531 name: 'background-gradient-stop-colors',
16532 type: t.colors
16533 }, {
16534 name: 'background-gradient-stop-positions',
16535 type: t.percentages
16536 }, {
16537 name: 'background-gradient-direction',
16538 type: t.gradientDirection
16539 }, {
16540 name: 'padding',
16541 type: t.sizeMaybePercent,
16542 triggersBounds: diff.any
16543 }, {
16544 name: 'padding-relative-to',
16545 type: t.paddingRelativeTo,
16546 triggersBounds: diff.any
16547 }, {
16548 name: 'bounds-expansion',
16549 type: t.boundsExpansion,
16550 triggersBounds: diff.any
16551 }];
16552 var nodeBorder = [{
16553 name: 'border-color',
16554 type: t.color
16555 }, {
16556 name: 'border-opacity',
16557 type: t.zeroOneNumber
16558 }, {
16559 name: 'border-width',
16560 type: t.size,
16561 triggersBounds: diff.any
16562 }, {
16563 name: 'border-style',
16564 type: t.borderStyle
16565 }];
16566 var backgroundImage = [{
16567 name: 'background-image',
16568 type: t.urls
16569 }, {
16570 name: 'background-image-crossorigin',
16571 type: t.bgCrossOrigin
16572 }, {
16573 name: 'background-image-opacity',
16574 type: t.zeroOneNumbers
16575 }, {
16576 name: 'background-position-x',
16577 type: t.bgPos
16578 }, {
16579 name: 'background-position-y',
16580 type: t.bgPos
16581 }, {
16582 name: 'background-width-relative-to',
16583 type: t.bgRelativeTo
16584 }, {
16585 name: 'background-height-relative-to',
16586 type: t.bgRelativeTo
16587 }, {
16588 name: 'background-repeat',
16589 type: t.bgRepeat
16590 }, {
16591 name: 'background-fit',
16592 type: t.bgFit
16593 }, {
16594 name: 'background-clip',
16595 type: t.bgClip
16596 }, {
16597 name: 'background-width',
16598 type: t.bgWH
16599 }, {
16600 name: 'background-height',
16601 type: t.bgWH
16602 }, {
16603 name: 'background-offset-x',
16604 type: t.bgPos
16605 }, {
16606 name: 'background-offset-y',
16607 type: t.bgPos
16608 }];
16609 var compound = [{
16610 name: 'position',
16611 type: t.position,
16612 triggersBounds: diff.any
16613 }, {
16614 name: 'compound-sizing-wrt-labels',
16615 type: t.compoundIncludeLabels,
16616 triggersBounds: diff.any
16617 }, {
16618 name: 'min-width',
16619 type: t.size,
16620 triggersBounds: diff.any
16621 }, {
16622 name: 'min-width-bias-left',
16623 type: t.sizeMaybePercent,
16624 triggersBounds: diff.any
16625 }, {
16626 name: 'min-width-bias-right',
16627 type: t.sizeMaybePercent,
16628 triggersBounds: diff.any
16629 }, {
16630 name: 'min-height',
16631 type: t.size,
16632 triggersBounds: diff.any
16633 }, {
16634 name: 'min-height-bias-top',
16635 type: t.sizeMaybePercent,
16636 triggersBounds: diff.any
16637 }, {
16638 name: 'min-height-bias-bottom',
16639 type: t.sizeMaybePercent,
16640 triggersBounds: diff.any
16641 }];
16642 var edgeLine = [{
16643 name: 'line-style',
16644 type: t.lineStyle
16645 }, {
16646 name: 'line-color',
16647 type: t.color
16648 }, {
16649 name: 'line-fill',
16650 type: t.fill
16651 }, {
16652 name: 'line-cap',
16653 type: t.lineCap
16654 }, {
16655 name: 'line-dash-pattern',
16656 type: t.numbers
16657 }, {
16658 name: 'line-dash-offset',
16659 type: t.number
16660 }, {
16661 name: 'line-gradient-stop-colors',
16662 type: t.colors
16663 }, {
16664 name: 'line-gradient-stop-positions',
16665 type: t.percentages
16666 }, {
16667 name: 'curve-style',
16668 type: t.curveStyle,
16669 triggersBounds: diff.any,
16670 triggersBoundsOfParallelBeziers: true
16671 }, {
16672 name: 'haystack-radius',
16673 type: t.zeroOneNumber,
16674 triggersBounds: diff.any
16675 }, {
16676 name: 'source-endpoint',
16677 type: t.edgeEndpoint,
16678 triggersBounds: diff.any
16679 }, {
16680 name: 'target-endpoint',
16681 type: t.edgeEndpoint,
16682 triggersBounds: diff.any
16683 }, {
16684 name: 'control-point-step-size',
16685 type: t.size,
16686 triggersBounds: diff.any
16687 }, {
16688 name: 'control-point-distances',
16689 type: t.bidirectionalSizes,
16690 triggersBounds: diff.any
16691 }, {
16692 name: 'control-point-weights',
16693 type: t.numbers,
16694 triggersBounds: diff.any
16695 }, {
16696 name: 'segment-distances',
16697 type: t.bidirectionalSizes,
16698 triggersBounds: diff.any
16699 }, {
16700 name: 'segment-weights',
16701 type: t.numbers,
16702 triggersBounds: diff.any
16703 }, {
16704 name: 'taxi-turn',
16705 type: t.bidirectionalSizeMaybePercent,
16706 triggersBounds: diff.any
16707 }, {
16708 name: 'taxi-turn-min-distance',
16709 type: t.size,
16710 triggersBounds: diff.any
16711 }, {
16712 name: 'taxi-direction',
16713 type: t.axisDirection,
16714 triggersBounds: diff.any
16715 }, {
16716 name: 'edge-distances',
16717 type: t.edgeDistances,
16718 triggersBounds: diff.any
16719 }, {
16720 name: 'arrow-scale',
16721 type: t.positiveNumber,
16722 triggersBounds: diff.any
16723 }, {
16724 name: 'loop-direction',
16725 type: t.angle,
16726 triggersBounds: diff.any
16727 }, {
16728 name: 'loop-sweep',
16729 type: t.angle,
16730 triggersBounds: diff.any
16731 }, {
16732 name: 'source-distance-from-node',
16733 type: t.size,
16734 triggersBounds: diff.any
16735 }, {
16736 name: 'target-distance-from-node',
16737 type: t.size,
16738 triggersBounds: diff.any
16739 }];
16740 var ghost = [{
16741 name: 'ghost',
16742 type: t.bool,
16743 triggersBounds: diff.any
16744 }, {
16745 name: 'ghost-offset-x',
16746 type: t.bidirectionalSize,
16747 triggersBounds: diff.any
16748 }, {
16749 name: 'ghost-offset-y',
16750 type: t.bidirectionalSize,
16751 triggersBounds: diff.any
16752 }, {
16753 name: 'ghost-opacity',
16754 type: t.zeroOneNumber
16755 }];
16756 var core = [{
16757 name: 'selection-box-color',
16758 type: t.color
16759 }, {
16760 name: 'selection-box-opacity',
16761 type: t.zeroOneNumber
16762 }, {
16763 name: 'selection-box-border-color',
16764 type: t.color
16765 }, {
16766 name: 'selection-box-border-width',
16767 type: t.size
16768 }, {
16769 name: 'active-bg-color',
16770 type: t.color
16771 }, {
16772 name: 'active-bg-opacity',
16773 type: t.zeroOneNumber
16774 }, {
16775 name: 'active-bg-size',
16776 type: t.size
16777 }, {
16778 name: 'outside-texture-bg-color',
16779 type: t.color
16780 }, {
16781 name: 'outside-texture-bg-opacity',
16782 type: t.zeroOneNumber
16783 }]; // pie backgrounds for nodes
16784
16785 var pie = [];
16786 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
16787
16788 pie.push({
16789 name: 'pie-size',
16790 type: t.sizeMaybePercent
16791 });
16792
16793 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
16794 pie.push({
16795 name: 'pie-' + i + '-background-color',
16796 type: t.color
16797 });
16798 pie.push({
16799 name: 'pie-' + i + '-background-size',
16800 type: t.percent
16801 });
16802 pie.push({
16803 name: 'pie-' + i + '-background-opacity',
16804 type: t.zeroOneNumber
16805 });
16806 } // edge arrows
16807
16808
16809 var edgeArrow = [];
16810 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
16811 [{
16812 name: 'arrow-shape',
16813 type: t.arrowShape,
16814 triggersBounds: diff.any
16815 }, {
16816 name: 'arrow-color',
16817 type: t.color
16818 }, {
16819 name: 'arrow-fill',
16820 type: t.arrowFill
16821 }].forEach(function (prop) {
16822 arrowPrefixes.forEach(function (prefix) {
16823 var name = prefix + '-' + prop.name;
16824 var type = prop.type,
16825 triggersBounds = prop.triggersBounds;
16826 edgeArrow.push({
16827 name: name,
16828 type: type,
16829 triggersBounds: triggersBounds
16830 });
16831 });
16832 }, {});
16833 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
16834 var propGroups = styfn$6.propertyGroups = {
16835 // common to all eles
16836 behavior: behavior,
16837 transition: transition,
16838 visibility: visibility,
16839 overlay: overlay,
16840 ghost: ghost,
16841 // labels
16842 commonLabel: commonLabel,
16843 labelDimensions: labelDimensions,
16844 mainLabel: mainLabel,
16845 sourceLabel: sourceLabel,
16846 targetLabel: targetLabel,
16847 // node props
16848 nodeBody: nodeBody,
16849 nodeBorder: nodeBorder,
16850 backgroundImage: backgroundImage,
16851 pie: pie,
16852 compound: compound,
16853 // edge props
16854 edgeLine: edgeLine,
16855 edgeArrow: edgeArrow,
16856 core: core
16857 };
16858 var propGroupNames = styfn$6.propertyGroupNames = {};
16859 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
16860 propGroupKeys.forEach(function (key) {
16861 propGroupNames[key] = propGroups[key].map(function (prop) {
16862 return prop.name;
16863 });
16864 propGroups[key].forEach(function (prop) {
16865 return prop.groupKey = key;
16866 });
16867 }); // define aliases
16868
16869 var aliases = styfn$6.aliases = [{
16870 name: 'content',
16871 pointsTo: 'label'
16872 }, {
16873 name: 'control-point-distance',
16874 pointsTo: 'control-point-distances'
16875 }, {
16876 name: 'control-point-weight',
16877 pointsTo: 'control-point-weights'
16878 }, {
16879 name: 'edge-text-rotation',
16880 pointsTo: 'text-rotation'
16881 }, {
16882 name: 'padding-left',
16883 pointsTo: 'padding'
16884 }, {
16885 name: 'padding-right',
16886 pointsTo: 'padding'
16887 }, {
16888 name: 'padding-top',
16889 pointsTo: 'padding'
16890 }, {
16891 name: 'padding-bottom',
16892 pointsTo: 'padding'
16893 }]; // list of property names
16894
16895 styfn$6.propertyNames = props.map(function (p) {
16896 return p.name;
16897 }); // allow access of properties by name ( e.g. style.properties.height )
16898
16899 for (var _i = 0; _i < props.length; _i++) {
16900 var prop = props[_i];
16901 props[prop.name] = prop; // allow lookup by name
16902 } // map aliases
16903
16904
16905 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
16906 var alias = aliases[_i2];
16907 var pointsToProp = props[alias.pointsTo];
16908 var aliasProp = {
16909 name: alias.name,
16910 alias: true,
16911 pointsTo: pointsToProp
16912 }; // add alias prop for parsing
16913
16914 props.push(aliasProp);
16915 props[alias.name] = aliasProp; // allow lookup by name
16916 }
16917})();
16918
16919styfn$6.getDefaultProperty = function (name) {
16920 return this.getDefaultProperties()[name];
16921};
16922
16923styfn$6.getDefaultProperties = function () {
16924 var _p = this._private;
16925
16926 if (_p.defaultProperties != null) {
16927 return _p.defaultProperties;
16928 }
16929
16930 var rawProps = extend({
16931 // core props
16932 'selection-box-color': '#ddd',
16933 'selection-box-opacity': 0.65,
16934 'selection-box-border-color': '#aaa',
16935 'selection-box-border-width': 1,
16936 'active-bg-color': 'black',
16937 'active-bg-opacity': 0.15,
16938 'active-bg-size': 30,
16939 'outside-texture-bg-color': '#000',
16940 'outside-texture-bg-opacity': 0.125,
16941 // common node/edge props
16942 'events': 'yes',
16943 'text-events': 'no',
16944 'text-valign': 'top',
16945 'text-halign': 'center',
16946 'text-justification': 'auto',
16947 'line-height': 1,
16948 'color': '#000',
16949 'text-outline-color': '#000',
16950 'text-outline-width': 0,
16951 'text-outline-opacity': 1,
16952 'text-opacity': 1,
16953 'text-decoration': 'none',
16954 'text-transform': 'none',
16955 'text-wrap': 'none',
16956 'text-overflow-wrap': 'whitespace',
16957 'text-max-width': 9999,
16958 'text-background-color': '#000',
16959 'text-background-opacity': 0,
16960 'text-background-shape': 'rectangle',
16961 'text-background-padding': 0,
16962 'text-border-opacity': 0,
16963 'text-border-width': 0,
16964 'text-border-style': 'solid',
16965 'text-border-color': '#000',
16966 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
16967 'font-style': 'normal',
16968 'font-weight': 'normal',
16969 'font-size': 16,
16970 'min-zoomed-font-size': 0,
16971 'text-rotation': 'none',
16972 'source-text-rotation': 'none',
16973 'target-text-rotation': 'none',
16974 'visibility': 'visible',
16975 'display': 'element',
16976 'opacity': 1,
16977 'z-compound-depth': 'auto',
16978 'z-index-compare': 'auto',
16979 'z-index': 0,
16980 'label': '',
16981 'text-margin-x': 0,
16982 'text-margin-y': 0,
16983 'source-label': '',
16984 'source-text-offset': 0,
16985 'source-text-margin-x': 0,
16986 'source-text-margin-y': 0,
16987 'target-label': '',
16988 'target-text-offset': 0,
16989 'target-text-margin-x': 0,
16990 'target-text-margin-y': 0,
16991 'overlay-opacity': 0,
16992 'overlay-color': '#000',
16993 'overlay-padding': 10,
16994 'transition-property': 'none',
16995 'transition-duration': 0,
16996 'transition-delay': 0,
16997 'transition-timing-function': 'linear',
16998 // node props
16999 'background-blacken': 0,
17000 'background-color': '#999',
17001 'background-fill': 'solid',
17002 'background-opacity': 1,
17003 'background-image': 'none',
17004 'background-image-crossorigin': 'anonymous',
17005 'background-image-opacity': 1,
17006 'background-position-x': '50%',
17007 'background-position-y': '50%',
17008 'background-offset-x': 0,
17009 'background-offset-y': 0,
17010 'background-width-relative-to': 'include-padding',
17011 'background-height-relative-to': 'include-padding',
17012 'background-repeat': 'no-repeat',
17013 'background-fit': 'none',
17014 'background-clip': 'node',
17015 'background-width': 'auto',
17016 'background-height': 'auto',
17017 'border-color': '#000',
17018 'border-opacity': 1,
17019 'border-width': 0,
17020 'border-style': 'solid',
17021 'height': 30,
17022 'width': 30,
17023 'shape': 'ellipse',
17024 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17025 'bounds-expansion': 0,
17026 // node gradient
17027 'background-gradient-direction': 'to-bottom',
17028 'background-gradient-stop-colors': '#999',
17029 'background-gradient-stop-positions': '0%',
17030 // ghost props
17031 'ghost': 'no',
17032 'ghost-offset-y': 0,
17033 'ghost-offset-x': 0,
17034 'ghost-opacity': 0,
17035 // compound props
17036 'padding': 0,
17037 'padding-relative-to': 'width',
17038 'position': 'origin',
17039 'compound-sizing-wrt-labels': 'include',
17040 'min-width': 0,
17041 'min-width-bias-left': 0,
17042 'min-width-bias-right': 0,
17043 'min-height': 0,
17044 'min-height-bias-top': 0,
17045 'min-height-bias-bottom': 0
17046 }, {
17047 // node pie bg
17048 'pie-size': '100%'
17049 }, [{
17050 name: 'pie-{{i}}-background-color',
17051 value: 'black'
17052 }, {
17053 name: 'pie-{{i}}-background-size',
17054 value: '0%'
17055 }, {
17056 name: 'pie-{{i}}-background-opacity',
17057 value: 1
17058 }].reduce(function (css, prop) {
17059 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17060 var name = prop.name.replace('{{i}}', i);
17061 var val = prop.value;
17062 css[name] = val;
17063 }
17064
17065 return css;
17066 }, {}), {
17067 // edge props
17068 'line-style': 'solid',
17069 'line-color': '#999',
17070 'line-fill': 'solid',
17071 'line-cap': 'butt',
17072 'line-gradient-stop-colors': '#999',
17073 'line-gradient-stop-positions': '0%',
17074 'control-point-step-size': 40,
17075 'control-point-weights': 0.5,
17076 'segment-weights': 0.5,
17077 'segment-distances': 20,
17078 'taxi-turn': '50%',
17079 'taxi-turn-min-distance': 10,
17080 'taxi-direction': 'auto',
17081 'edge-distances': 'intersection',
17082 'curve-style': 'haystack',
17083 'haystack-radius': 0,
17084 'arrow-scale': 1,
17085 'loop-direction': '-45deg',
17086 'loop-sweep': '-90deg',
17087 'source-distance-from-node': 0,
17088 'target-distance-from-node': 0,
17089 'source-endpoint': 'outside-to-node',
17090 'target-endpoint': 'outside-to-node',
17091 'line-dash-pattern': [6, 3],
17092 'line-dash-offset': 0
17093 }, [{
17094 name: 'arrow-shape',
17095 value: 'none'
17096 }, {
17097 name: 'arrow-color',
17098 value: '#999'
17099 }, {
17100 name: 'arrow-fill',
17101 value: 'filled'
17102 }].reduce(function (css, prop) {
17103 styfn$6.arrowPrefixes.forEach(function (prefix) {
17104 var name = prefix + '-' + prop.name;
17105 var val = prop.value;
17106 css[name] = val;
17107 });
17108 return css;
17109 }, {}));
17110 var parsedProps = {};
17111
17112 for (var i = 0; i < this.properties.length; i++) {
17113 var prop = this.properties[i];
17114
17115 if (prop.pointsTo) {
17116 continue;
17117 }
17118
17119 var name = prop.name;
17120 var val = rawProps[name];
17121 var parsedProp = this.parse(name, val);
17122 parsedProps[name] = parsedProp;
17123 }
17124
17125 _p.defaultProperties = parsedProps;
17126 return _p.defaultProperties;
17127};
17128
17129styfn$6.addDefaultStylesheet = function () {
17130 this.selector(':parent').css({
17131 'shape': 'rectangle',
17132 'padding': 10,
17133 'background-color': '#eee',
17134 'border-color': '#ccc',
17135 'border-width': 1
17136 }).selector('edge').css({
17137 'width': 3
17138 }).selector(':loop').css({
17139 'curve-style': 'bezier'
17140 }).selector('edge:compound').css({
17141 'curve-style': 'bezier',
17142 'source-endpoint': 'outside-to-line',
17143 'target-endpoint': 'outside-to-line'
17144 }).selector(':selected').css({
17145 'background-color': '#0169D9',
17146 'line-color': '#0169D9',
17147 'source-arrow-color': '#0169D9',
17148 'target-arrow-color': '#0169D9',
17149 'mid-source-arrow-color': '#0169D9',
17150 'mid-target-arrow-color': '#0169D9'
17151 }).selector(':parent:selected').css({
17152 'background-color': '#CCE1F9',
17153 'border-color': '#aec8e5'
17154 }).selector(':active').css({
17155 'overlay-color': 'black',
17156 'overlay-padding': 10,
17157 'overlay-opacity': 0.25
17158 });
17159 this.defaultLength = this.length;
17160};
17161
17162var styfn$7 = {}; // a caching layer for property parsing
17163
17164styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
17165 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17166
17167 if (fn(value)) {
17168 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17169 }
17170
17171 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17172 var bypassKey = propIsBypass ? 't' : 'f';
17173 var valueKey = '' + value;
17174 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17175 var propCache = self.propCache = self.propCache || [];
17176 var ret;
17177
17178 if (!(ret = propCache[argHash])) {
17179 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17180 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17181 // - mappings can't be shared b/c mappings are per-element
17182
17183
17184 if (propIsBypass || propIsFlat === 'mapping') {
17185 // need a copy since props are mutated later in their lifecycles
17186 ret = copy(ret);
17187
17188 if (ret) {
17189 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17190 }
17191 }
17192
17193 return ret;
17194};
17195
17196styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17197 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17198
17199 if (!prop && value != null) {
17200 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17201 }
17202
17203 return prop;
17204}; // parse a property; return null on invalid; return parsed property otherwise
17205// fields :
17206// - name : the name of the property
17207// - value : the parsed, native-typed value of the property
17208// - strValue : a string value that represents the property value in valid css
17209// - bypass : true iff the property is a bypass property
17210
17211
17212styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17213 var self = this;
17214 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17215
17216 var property = self.properties[name];
17217 var passedValue = value;
17218 var types = self.types;
17219
17220 if (!property) {
17221 return null;
17222 } // return null on property of unknown name
17223
17224
17225 if (value === undefined) {
17226 return null;
17227 } // can't assign undefined
17228 // the property may be an alias
17229
17230
17231 if (property.alias) {
17232 property = property.pointsTo;
17233 name = property.name;
17234 }
17235
17236 var valueIsString = string(value);
17237
17238 if (valueIsString) {
17239 // trim the value to make parsing easier
17240 value = value.trim();
17241 }
17242
17243 var type = property.type;
17244
17245 if (!type) {
17246 return null;
17247 } // no type, no luck
17248 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17249
17250
17251 if (propIsBypass && (value === '' || value === null)) {
17252 return {
17253 name: name,
17254 value: value,
17255 bypass: true,
17256 deleteBypass: true
17257 };
17258 } // check if value is a function used as a mapper
17259
17260
17261 if (fn(value)) {
17262 return {
17263 name: name,
17264 value: value,
17265 strValue: 'fn',
17266 mapped: types.fn,
17267 bypass: propIsBypass
17268 };
17269 } // check if value is mapped
17270
17271
17272 var data, mapData;
17273
17274 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))) {
17275 if (propIsBypass) {
17276 return false;
17277 } // mappers not allowed in bypass
17278
17279
17280 var mapped = types.data;
17281 return {
17282 name: name,
17283 value: data,
17284 strValue: '' + value,
17285 mapped: mapped,
17286 field: data[1],
17287 bypass: propIsBypass
17288 };
17289 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17290 if (propIsBypass) {
17291 return false;
17292 } // mappers not allowed in bypass
17293
17294
17295 if (type.multiple) {
17296 return false;
17297 } // impossible to map to num
17298
17299
17300 var _mapped = types.mapData; // we can map only if the type is a colour or a number
17301
17302 if (!(type.color || type.number)) {
17303 return false;
17304 }
17305
17306 var valueMin = this.parse(name, mapData[4]); // parse to validate
17307
17308 if (!valueMin || valueMin.mapped) {
17309 return false;
17310 } // can't be invalid or mapped
17311
17312
17313 var valueMax = this.parse(name, mapData[5]); // parse to validate
17314
17315 if (!valueMax || valueMax.mapped) {
17316 return false;
17317 } // can't be invalid or mapped
17318 // check if valueMin and valueMax are the same
17319
17320
17321 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17322 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17323 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17324 } else if (type.color) {
17325 var c1 = valueMin.value;
17326 var c2 = valueMax.value;
17327 var same = c1[0] === c2[0] // red
17328 && c1[1] === c2[1] // green
17329 && c1[2] === c2[2] // blue
17330 && ( // optional alpha
17331 c1[3] === c2[3] // same alpha outright
17332 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
17333 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17334 );
17335
17336 if (same) {
17337 return false;
17338 } // can't make a mapper without a range
17339
17340 }
17341
17342 return {
17343 name: name,
17344 value: mapData,
17345 strValue: '' + value,
17346 mapped: _mapped,
17347 field: mapData[1],
17348 fieldMin: parseFloat(mapData[2]),
17349 // min & max are numeric
17350 fieldMax: parseFloat(mapData[3]),
17351 valueMin: valueMin.value,
17352 valueMax: valueMax.value,
17353 bypass: propIsBypass
17354 };
17355 }
17356
17357 if (type.multiple && propIsFlat !== 'multiple') {
17358 var vals;
17359
17360 if (valueIsString) {
17361 vals = value.split(/\s+/);
17362 } else if (array(value)) {
17363 vals = value;
17364 } else {
17365 vals = [value];
17366 }
17367
17368 if (type.evenMultiple && vals.length % 2 !== 0) {
17369 return null;
17370 }
17371
17372 var valArr = [];
17373 var unitsArr = [];
17374 var pfValArr = [];
17375 var strVal = '';
17376 var hasEnum = false;
17377
17378 for (var i = 0; i < vals.length; i++) {
17379 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17380 hasEnum = hasEnum || string(p.value);
17381 valArr.push(p.value);
17382 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17383 unitsArr.push(p.units);
17384 strVal += (i > 0 ? ' ' : '') + p.strValue;
17385 }
17386
17387 if (type.validate && !type.validate(valArr, unitsArr)) {
17388 return null;
17389 }
17390
17391 if (type.singleEnum && hasEnum) {
17392 if (valArr.length === 1 && string(valArr[0])) {
17393 return {
17394 name: name,
17395 value: valArr[0],
17396 strValue: valArr[0],
17397 bypass: propIsBypass
17398 };
17399 } else {
17400 return null;
17401 }
17402 }
17403
17404 return {
17405 name: name,
17406 value: valArr,
17407 pfValue: pfValArr,
17408 strValue: strVal,
17409 bypass: propIsBypass,
17410 units: unitsArr
17411 };
17412 } // several types also allow enums
17413
17414
17415 var checkEnums = function checkEnums() {
17416 for (var _i = 0; _i < type.enums.length; _i++) {
17417 var en = type.enums[_i];
17418
17419 if (en === value) {
17420 return {
17421 name: name,
17422 value: value,
17423 strValue: '' + value,
17424 bypass: propIsBypass
17425 };
17426 }
17427 }
17428
17429 return null;
17430 }; // check the type and return the appropriate object
17431
17432
17433 if (type.number) {
17434 var units;
17435 var implicitUnits = 'px'; // not set => px
17436
17437 if (type.units) {
17438 // use specified units if set
17439 units = type.units;
17440 }
17441
17442 if (type.implicitUnits) {
17443 implicitUnits = type.implicitUnits;
17444 }
17445
17446 if (!type.unitless) {
17447 if (valueIsString) {
17448 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17449
17450 if (units) {
17451 unitsRegex = units;
17452 } // only allow explicit units if so set
17453
17454
17455 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
17456
17457 if (match) {
17458 value = match[1];
17459 units = match[2] || implicitUnits;
17460 }
17461 } else if (!units || type.implicitUnits) {
17462 units = implicitUnits; // implicitly px if unspecified
17463 }
17464 }
17465
17466 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
17467
17468 if (isNaN(value) && type.enums === undefined) {
17469 return null;
17470 } // check if this number type also accepts special keywords in place of numbers
17471 // (i.e. `left`, `auto`, etc)
17472
17473
17474 if (isNaN(value) && type.enums !== undefined) {
17475 value = passedValue;
17476 return checkEnums();
17477 } // check if value must be an integer
17478
17479
17480 if (type.integer && !integer(value)) {
17481 return null;
17482 } // check value is within range
17483
17484
17485 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17486 return null;
17487 }
17488
17489 var ret = {
17490 name: name,
17491 value: value,
17492 strValue: '' + value + (units ? units : ''),
17493 units: units,
17494 bypass: propIsBypass
17495 }; // normalise value in pixels
17496
17497 if (type.unitless || units !== 'px' && units !== 'em') {
17498 ret.pfValue = value;
17499 } else {
17500 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17501 } // normalise value in ms
17502
17503
17504 if (units === 'ms' || units === 's') {
17505 ret.pfValue = units === 'ms' ? value : 1000 * value;
17506 } // normalise value in rad
17507
17508
17509 if (units === 'deg' || units === 'rad') {
17510 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17511 } // normalize value in %
17512
17513
17514 if (units === '%') {
17515 ret.pfValue = value / 100;
17516 }
17517
17518 return ret;
17519 } else if (type.propList) {
17520 var props = [];
17521 var propsStr = '' + value;
17522
17523 if (propsStr === 'none') ; else {
17524 // go over each prop
17525 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17526
17527 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17528 var propName = propsSplit[_i2].trim();
17529
17530 if (self.properties[propName]) {
17531 props.push(propName);
17532 } else {
17533 warn('`' + propName + '` is not a valid property name');
17534 }
17535 }
17536
17537 if (props.length === 0) {
17538 return null;
17539 }
17540 }
17541
17542 return {
17543 name: name,
17544 value: props,
17545 strValue: props.length === 0 ? 'none' : props.join(' '),
17546 bypass: propIsBypass
17547 };
17548 } else if (type.color) {
17549 var tuple = color2tuple(value);
17550
17551 if (!tuple) {
17552 return null;
17553 }
17554
17555 return {
17556 name: name,
17557 value: tuple,
17558 pfValue: tuple,
17559 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
17560 // n.b. no spaces b/c of multiple support
17561 bypass: propIsBypass
17562 };
17563 } else if (type.regex || type.regexes) {
17564 // first check enums
17565 if (type.enums) {
17566 var enumProp = checkEnums();
17567
17568 if (enumProp) {
17569 return enumProp;
17570 }
17571 }
17572
17573 var regexes = type.regexes ? type.regexes : [type.regex];
17574
17575 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
17576 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
17577
17578 var m = regex.exec(value);
17579
17580 if (m) {
17581 // regex matches
17582 return {
17583 name: name,
17584 value: type.singleRegexMatchValue ? m[1] : m,
17585 strValue: '' + value,
17586 bypass: propIsBypass
17587 };
17588 }
17589 }
17590
17591 return null; // didn't match any
17592 } else if (type.string) {
17593 // just return
17594 return {
17595 name: name,
17596 value: '' + value,
17597 strValue: '' + value,
17598 bypass: propIsBypass
17599 };
17600 } else if (type.enums) {
17601 // check enums last because it's a combo type in others
17602 return checkEnums();
17603 } else {
17604 return null; // not a type we can handle
17605 }
17606};
17607
17608var Style = function Style(cy) {
17609 if (!(this instanceof Style)) {
17610 return new Style(cy);
17611 }
17612
17613 if (!core(cy)) {
17614 error('A style must have a core reference');
17615 return;
17616 }
17617
17618 this._private = {
17619 cy: cy,
17620 coreStyle: {}
17621 };
17622 this.length = 0;
17623 this.resetToDefault();
17624};
17625
17626var styfn$8 = Style.prototype;
17627
17628styfn$8.instanceString = function () {
17629 return 'style';
17630}; // remove all contexts
17631
17632
17633styfn$8.clear = function () {
17634 for (var i = 0; i < this.length; i++) {
17635 this[i] = undefined;
17636 }
17637
17638 this.length = 0;
17639 var _p = this._private;
17640 _p.newStyle = true;
17641 return this; // chaining
17642};
17643
17644styfn$8.resetToDefault = function () {
17645 this.clear();
17646 this.addDefaultStylesheet();
17647 return this;
17648}; // builds a style object for the 'core' selector
17649
17650
17651styfn$8.core = function (propName) {
17652 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
17653}; // create a new context from the specified selector string and switch to that context
17654
17655
17656styfn$8.selector = function (selectorStr) {
17657 // 'core' is a special case and does not need a selector
17658 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
17659 var i = this.length++; // new context means new index
17660
17661 this[i] = {
17662 selector: selector,
17663 properties: [],
17664 mappedProperties: [],
17665 index: i
17666 };
17667 return this; // chaining
17668}; // add one or many css rules to the current context
17669
17670
17671styfn$8.css = function () {
17672 var self = this;
17673 var args = arguments;
17674
17675 if (args.length === 1) {
17676 var map = args[0];
17677
17678 for (var i = 0; i < self.properties.length; i++) {
17679 var prop = self.properties[i];
17680 var mapVal = map[prop.name];
17681
17682 if (mapVal === undefined) {
17683 mapVal = map[dash2camel(prop.name)];
17684 }
17685
17686 if (mapVal !== undefined) {
17687 this.cssRule(prop.name, mapVal);
17688 }
17689 }
17690 } else if (args.length === 2) {
17691 this.cssRule(args[0], args[1]);
17692 } // do nothing if args are invalid
17693
17694
17695 return this; // chaining
17696};
17697
17698styfn$8.style = styfn$8.css; // add a single css rule to the current context
17699
17700styfn$8.cssRule = function (name, value) {
17701 // name-value pair
17702 var property = this.parse(name, value); // add property to current context if valid
17703
17704 if (property) {
17705 var i = this.length - 1;
17706 this[i].properties.push(property);
17707 this[i].properties[property.name] = property; // allow access by name as well
17708
17709 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
17710 this._private.hasPie = true;
17711 }
17712
17713 if (property.mapped) {
17714 this[i].mappedProperties.push(property);
17715 } // add to core style if necessary
17716
17717
17718 var currentSelectorIsCore = !this[i].selector;
17719
17720 if (currentSelectorIsCore) {
17721 this._private.coreStyle[property.name] = property;
17722 }
17723 }
17724
17725 return this; // chaining
17726};
17727
17728styfn$8.append = function (style) {
17729 if (stylesheet(style)) {
17730 style.appendToStyle(this);
17731 } else if (array(style)) {
17732 this.appendFromJson(style);
17733 } else if (string(style)) {
17734 this.appendFromString(style);
17735 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
17736
17737
17738 return this;
17739}; // static function
17740
17741
17742Style.fromJson = function (cy, json) {
17743 var style = new Style(cy);
17744 style.fromJson(json);
17745 return style;
17746};
17747
17748Style.fromString = function (cy, string) {
17749 return new Style(cy).fromString(string);
17750};
17751
17752[styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
17753 extend(styfn$8, props);
17754});
17755Style.types = styfn$8.types;
17756Style.properties = styfn$8.properties;
17757Style.propertyGroups = styfn$8.propertyGroups;
17758Style.propertyGroupNames = styfn$8.propertyGroupNames;
17759Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
17760
17761var corefn$7 = {
17762 style: function style(newStyle) {
17763 if (newStyle) {
17764 var s = this.setStyle(newStyle);
17765 s.update();
17766 }
17767
17768 return this._private.style;
17769 },
17770 setStyle: function setStyle(style) {
17771 var _p = this._private;
17772
17773 if (stylesheet(style)) {
17774 _p.style = style.generateStyle(this);
17775 } else if (array(style)) {
17776 _p.style = Style.fromJson(this, style);
17777 } else if (string(style)) {
17778 _p.style = Style.fromString(this, style);
17779 } else {
17780 _p.style = Style(this);
17781 }
17782
17783 return _p.style;
17784 }
17785};
17786
17787var defaultSelectionType = 'single';
17788var corefn$8 = {
17789 autolock: function autolock(bool) {
17790 if (bool !== undefined) {
17791 this._private.autolock = bool ? true : false;
17792 } else {
17793 return this._private.autolock;
17794 }
17795
17796 return this; // chaining
17797 },
17798 autoungrabify: function autoungrabify(bool) {
17799 if (bool !== undefined) {
17800 this._private.autoungrabify = bool ? true : false;
17801 } else {
17802 return this._private.autoungrabify;
17803 }
17804
17805 return this; // chaining
17806 },
17807 autounselectify: function autounselectify(bool) {
17808 if (bool !== undefined) {
17809 this._private.autounselectify = bool ? true : false;
17810 } else {
17811 return this._private.autounselectify;
17812 }
17813
17814 return this; // chaining
17815 },
17816 selectionType: function selectionType(selType) {
17817 var _p = this._private;
17818
17819 if (_p.selectionType == null) {
17820 _p.selectionType = defaultSelectionType;
17821 }
17822
17823 if (selType !== undefined) {
17824 if (selType === 'additive' || selType === 'single') {
17825 _p.selectionType = selType;
17826 }
17827 } else {
17828 return _p.selectionType;
17829 }
17830
17831 return this;
17832 },
17833 panningEnabled: function panningEnabled(bool) {
17834 if (bool !== undefined) {
17835 this._private.panningEnabled = bool ? true : false;
17836 } else {
17837 return this._private.panningEnabled;
17838 }
17839
17840 return this; // chaining
17841 },
17842 userPanningEnabled: function userPanningEnabled(bool) {
17843 if (bool !== undefined) {
17844 this._private.userPanningEnabled = bool ? true : false;
17845 } else {
17846 return this._private.userPanningEnabled;
17847 }
17848
17849 return this; // chaining
17850 },
17851 zoomingEnabled: function zoomingEnabled(bool) {
17852 if (bool !== undefined) {
17853 this._private.zoomingEnabled = bool ? true : false;
17854 } else {
17855 return this._private.zoomingEnabled;
17856 }
17857
17858 return this; // chaining
17859 },
17860 userZoomingEnabled: function userZoomingEnabled(bool) {
17861 if (bool !== undefined) {
17862 this._private.userZoomingEnabled = bool ? true : false;
17863 } else {
17864 return this._private.userZoomingEnabled;
17865 }
17866
17867 return this; // chaining
17868 },
17869 boxSelectionEnabled: function boxSelectionEnabled(bool) {
17870 if (bool !== undefined) {
17871 this._private.boxSelectionEnabled = bool ? true : false;
17872 } else {
17873 return this._private.boxSelectionEnabled;
17874 }
17875
17876 return this; // chaining
17877 },
17878 pan: function pan() {
17879 var args = arguments;
17880 var pan = this._private.pan;
17881 var dim, val, dims, x, y;
17882
17883 switch (args.length) {
17884 case 0:
17885 // .pan()
17886 return pan;
17887
17888 case 1:
17889 if (string(args[0])) {
17890 // .pan('x')
17891 dim = args[0];
17892 return pan[dim];
17893 } else if (plainObject(args[0])) {
17894 // .pan({ x: 0, y: 100 })
17895 if (!this._private.panningEnabled) {
17896 return this;
17897 }
17898
17899 dims = args[0];
17900 x = dims.x;
17901 y = dims.y;
17902
17903 if (number(x)) {
17904 pan.x = x;
17905 }
17906
17907 if (number(y)) {
17908 pan.y = y;
17909 }
17910
17911 this.emit('pan viewport');
17912 }
17913
17914 break;
17915
17916 case 2:
17917 // .pan('x', 100)
17918 if (!this._private.panningEnabled) {
17919 return this;
17920 }
17921
17922 dim = args[0];
17923 val = args[1];
17924
17925 if ((dim === 'x' || dim === 'y') && number(val)) {
17926 pan[dim] = val;
17927 }
17928
17929 this.emit('pan viewport');
17930 break;
17931 // invalid
17932 }
17933
17934 this.notify('viewport');
17935 return this; // chaining
17936 },
17937 panBy: function panBy(arg0, arg1) {
17938 var args = arguments;
17939 var pan = this._private.pan;
17940 var dim, val, dims, x, y;
17941
17942 if (!this._private.panningEnabled) {
17943 return this;
17944 }
17945
17946 switch (args.length) {
17947 case 1:
17948 if (plainObject(arg0)) {
17949 // .panBy({ x: 0, y: 100 })
17950 dims = args[0];
17951 x = dims.x;
17952 y = dims.y;
17953
17954 if (number(x)) {
17955 pan.x += x;
17956 }
17957
17958 if (number(y)) {
17959 pan.y += y;
17960 }
17961
17962 this.emit('pan viewport');
17963 }
17964
17965 break;
17966
17967 case 2:
17968 // .panBy('x', 100)
17969 dim = arg0;
17970 val = arg1;
17971
17972 if ((dim === 'x' || dim === 'y') && number(val)) {
17973 pan[dim] += val;
17974 }
17975
17976 this.emit('pan viewport');
17977 break;
17978 // invalid
17979 }
17980
17981 this.notify('viewport');
17982 return this; // chaining
17983 },
17984 fit: function fit(elements, padding) {
17985 var viewportState = this.getFitViewport(elements, padding);
17986
17987 if (viewportState) {
17988 var _p = this._private;
17989 _p.zoom = viewportState.zoom;
17990 _p.pan = viewportState.pan;
17991 this.emit('pan zoom viewport');
17992 this.notify('viewport');
17993 }
17994
17995 return this; // chaining
17996 },
17997 getFitViewport: function getFitViewport(elements, padding) {
17998 if (number(elements) && padding === undefined) {
17999 // elements is optional
18000 padding = elements;
18001 elements = undefined;
18002 }
18003
18004 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18005 return;
18006 }
18007
18008 var bb;
18009
18010 if (string(elements)) {
18011 var sel = elements;
18012 elements = this.$(sel);
18013 } else if (boundingBox(elements)) {
18014 // assume bb
18015 var bbe = elements;
18016 bb = {
18017 x1: bbe.x1,
18018 y1: bbe.y1,
18019 x2: bbe.x2,
18020 y2: bbe.y2
18021 };
18022 bb.w = bb.x2 - bb.x1;
18023 bb.h = bb.y2 - bb.y1;
18024 } else if (!elementOrCollection(elements)) {
18025 elements = this.mutableElements();
18026 }
18027
18028 if (elementOrCollection(elements) && elements.empty()) {
18029 return;
18030 } // can't fit to nothing
18031
18032
18033 bb = bb || elements.boundingBox();
18034 var w = this.width();
18035 var h = this.height();
18036 var zoom;
18037 padding = number(padding) ? padding : 0;
18038
18039 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18040 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18041
18042 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18043 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18044 var pan = {
18045 // now pan to middle
18046 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18047 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18048 };
18049 return {
18050 zoom: zoom,
18051 pan: pan
18052 };
18053 }
18054
18055 return;
18056 },
18057 zoomRange: function zoomRange(min, max) {
18058 var _p = this._private;
18059
18060 if (max == null) {
18061 var opts = min;
18062 min = opts.min;
18063 max = opts.max;
18064 }
18065
18066 if (number(min) && number(max) && min <= max) {
18067 _p.minZoom = min;
18068 _p.maxZoom = max;
18069 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
18070 _p.minZoom = min;
18071 } else if (number(max) && min === undefined && max >= _p.minZoom) {
18072 _p.maxZoom = max;
18073 }
18074
18075 return this;
18076 },
18077 minZoom: function minZoom(zoom) {
18078 if (zoom === undefined) {
18079 return this._private.minZoom;
18080 } else {
18081 return this.zoomRange({
18082 min: zoom
18083 });
18084 }
18085 },
18086 maxZoom: function maxZoom(zoom) {
18087 if (zoom === undefined) {
18088 return this._private.maxZoom;
18089 } else {
18090 return this.zoomRange({
18091 max: zoom
18092 });
18093 }
18094 },
18095 getZoomedViewport: function getZoomedViewport(params) {
18096 var _p = this._private;
18097 var currentPan = _p.pan;
18098 var currentZoom = _p.zoom;
18099 var pos; // in rendered px
18100
18101 var zoom;
18102 var bail = false;
18103
18104 if (!_p.zoomingEnabled) {
18105 // zooming disabled
18106 bail = true;
18107 }
18108
18109 if (number(params)) {
18110 // then set the zoom
18111 zoom = params;
18112 } else if (plainObject(params)) {
18113 // then zoom about a point
18114 zoom = params.level;
18115
18116 if (params.position != null) {
18117 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18118 } else if (params.renderedPosition != null) {
18119 pos = params.renderedPosition;
18120 }
18121
18122 if (pos != null && !_p.panningEnabled) {
18123 // panning disabled
18124 bail = true;
18125 }
18126 } // crop zoom
18127
18128
18129 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18130 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18131
18132 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
18133 return null;
18134 }
18135
18136 if (pos != null) {
18137 // set zoom about position
18138 var pan1 = currentPan;
18139 var zoom1 = currentZoom;
18140 var zoom2 = zoom;
18141 var pan2 = {
18142 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18143 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18144 };
18145 return {
18146 zoomed: true,
18147 panned: true,
18148 zoom: zoom2,
18149 pan: pan2
18150 };
18151 } else {
18152 // just set the zoom
18153 return {
18154 zoomed: true,
18155 panned: false,
18156 zoom: zoom,
18157 pan: currentPan
18158 };
18159 }
18160 },
18161 zoom: function zoom(params) {
18162 if (params === undefined) {
18163 // get
18164 return this._private.zoom;
18165 } else {
18166 // set
18167 var vp = this.getZoomedViewport(params);
18168 var _p = this._private;
18169
18170 if (vp == null || !vp.zoomed) {
18171 return this;
18172 }
18173
18174 _p.zoom = vp.zoom;
18175
18176 if (vp.panned) {
18177 _p.pan.x = vp.pan.x;
18178 _p.pan.y = vp.pan.y;
18179 }
18180
18181 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18182 this.notify('viewport');
18183 return this; // chaining
18184 }
18185 },
18186 viewport: function viewport(opts) {
18187 var _p = this._private;
18188 var zoomDefd = true;
18189 var panDefd = true;
18190 var events = []; // to trigger
18191
18192 var zoomFailed = false;
18193 var panFailed = false;
18194
18195 if (!opts) {
18196 return this;
18197 }
18198
18199 if (!number(opts.zoom)) {
18200 zoomDefd = false;
18201 }
18202
18203 if (!plainObject(opts.pan)) {
18204 panDefd = false;
18205 }
18206
18207 if (!zoomDefd && !panDefd) {
18208 return this;
18209 }
18210
18211 if (zoomDefd) {
18212 var z = opts.zoom;
18213
18214 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18215 zoomFailed = true;
18216 } else {
18217 _p.zoom = z;
18218 events.push('zoom');
18219 }
18220 }
18221
18222 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18223 var p = opts.pan;
18224
18225 if (number(p.x)) {
18226 _p.pan.x = p.x;
18227 panFailed = false;
18228 }
18229
18230 if (number(p.y)) {
18231 _p.pan.y = p.y;
18232 panFailed = false;
18233 }
18234
18235 if (!panFailed) {
18236 events.push('pan');
18237 }
18238 }
18239
18240 if (events.length > 0) {
18241 events.push('viewport');
18242 this.emit(events.join(' '));
18243 this.notify('viewport');
18244 }
18245
18246 return this; // chaining
18247 },
18248 center: function center(elements) {
18249 var pan = this.getCenterPan(elements);
18250
18251 if (pan) {
18252 this._private.pan = pan;
18253 this.emit('pan viewport');
18254 this.notify('viewport');
18255 }
18256
18257 return this; // chaining
18258 },
18259 getCenterPan: function getCenterPan(elements, zoom) {
18260 if (!this._private.panningEnabled) {
18261 return;
18262 }
18263
18264 if (string(elements)) {
18265 var selector = elements;
18266 elements = this.mutableElements().filter(selector);
18267 } else if (!elementOrCollection(elements)) {
18268 elements = this.mutableElements();
18269 }
18270
18271 if (elements.length === 0) {
18272 return;
18273 } // can't centre pan to nothing
18274
18275
18276 var bb = elements.boundingBox();
18277 var w = this.width();
18278 var h = this.height();
18279 zoom = zoom === undefined ? this._private.zoom : zoom;
18280 var pan = {
18281 // middle
18282 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18283 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18284 };
18285 return pan;
18286 },
18287 reset: function reset() {
18288 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18289 return this;
18290 }
18291
18292 this.viewport({
18293 pan: {
18294 x: 0,
18295 y: 0
18296 },
18297 zoom: 1
18298 });
18299 return this; // chaining
18300 },
18301 invalidateSize: function invalidateSize() {
18302 this._private.sizeCache = null;
18303 },
18304 size: function size() {
18305 var _p = this._private;
18306 var container = _p.container;
18307 return _p.sizeCache = _p.sizeCache || (container ? function () {
18308 var style = window$1.getComputedStyle(container);
18309
18310 var val = function val(name) {
18311 return parseFloat(style.getPropertyValue(name));
18312 };
18313
18314 return {
18315 width: container.clientWidth - val('padding-left') - val('padding-right'),
18316 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18317 };
18318 }() : {
18319 // fallback if no container (not 0 b/c can be used for dividing etc)
18320 width: 1,
18321 height: 1
18322 });
18323 },
18324 width: function width() {
18325 return this.size().width;
18326 },
18327 height: function height() {
18328 return this.size().height;
18329 },
18330 extent: function extent() {
18331 var pan = this._private.pan;
18332 var zoom = this._private.zoom;
18333 var rb = this.renderedExtent();
18334 var b = {
18335 x1: (rb.x1 - pan.x) / zoom,
18336 x2: (rb.x2 - pan.x) / zoom,
18337 y1: (rb.y1 - pan.y) / zoom,
18338 y2: (rb.y2 - pan.y) / zoom
18339 };
18340 b.w = b.x2 - b.x1;
18341 b.h = b.y2 - b.y1;
18342 return b;
18343 },
18344 renderedExtent: function renderedExtent() {
18345 var width = this.width();
18346 var height = this.height();
18347 return {
18348 x1: 0,
18349 y1: 0,
18350 x2: width,
18351 y2: height,
18352 w: width,
18353 h: height
18354 };
18355 }
18356}; // aliases
18357
18358corefn$8.centre = corefn$8.center; // backwards compatibility
18359
18360corefn$8.autolockNodes = corefn$8.autolock;
18361corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
18362
18363var fn$6 = {
18364 data: define$3.data({
18365 field: 'data',
18366 bindingEvent: 'data',
18367 allowBinding: true,
18368 allowSetting: true,
18369 settingEvent: 'data',
18370 settingTriggersEvent: true,
18371 triggerFnName: 'trigger',
18372 allowGetting: true
18373 }),
18374 removeData: define$3.removeData({
18375 field: 'data',
18376 event: 'data',
18377 triggerFnName: 'trigger',
18378 triggerEvent: true
18379 }),
18380 scratch: define$3.data({
18381 field: 'scratch',
18382 bindingEvent: 'scratch',
18383 allowBinding: true,
18384 allowSetting: true,
18385 settingEvent: 'scratch',
18386 settingTriggersEvent: true,
18387 triggerFnName: 'trigger',
18388 allowGetting: true
18389 }),
18390 removeScratch: define$3.removeData({
18391 field: 'scratch',
18392 event: 'scratch',
18393 triggerFnName: 'trigger',
18394 triggerEvent: true
18395 })
18396}; // aliases
18397
18398fn$6.attr = fn$6.data;
18399fn$6.removeAttr = fn$6.removeData;
18400
18401var Core = function Core(opts) {
18402 var cy = this;
18403 opts = extend({}, opts);
18404 var container = opts.container; // allow for passing a wrapped jquery object
18405 // e.g. cytoscape({ container: $('#cy') })
18406
18407 if (container && !htmlElement(container) && htmlElement(container[0])) {
18408 container = container[0];
18409 }
18410
18411 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18412
18413 reg = reg || {};
18414
18415 if (reg && reg.cy) {
18416 reg.cy.destroy();
18417 reg = {}; // old instance => replace reg completely
18418 }
18419
18420 var readies = reg.readies = reg.readies || [];
18421
18422 if (container) {
18423 container._cyreg = reg;
18424 } // make sure container assoc'd reg points to this cy
18425
18426
18427 reg.cy = cy;
18428 var head = window$1 !== undefined && container !== undefined && !opts.headless;
18429 var options = opts;
18430 options.layout = extend({
18431 name: head ? 'grid' : 'null'
18432 }, options.layout);
18433 options.renderer = extend({
18434 name: head ? 'canvas' : 'null'
18435 }, options.renderer);
18436
18437 var defVal = function defVal(def, val, altVal) {
18438 if (val !== undefined) {
18439 return val;
18440 } else if (altVal !== undefined) {
18441 return altVal;
18442 } else {
18443 return def;
18444 }
18445 };
18446
18447 var _p = this._private = {
18448 container: container,
18449 // html dom ele container
18450 ready: false,
18451 // whether ready has been triggered
18452 options: options,
18453 // cached options
18454 elements: new Collection(this),
18455 // elements in the graph
18456 listeners: [],
18457 // list of listeners
18458 aniEles: new Collection(this),
18459 // elements being animated
18460 data: {},
18461 // data for the core
18462 scratch: {},
18463 // scratch object for core
18464 layout: null,
18465 renderer: null,
18466 destroyed: false,
18467 // whether destroy was called
18468 notificationsEnabled: true,
18469 // whether notifications are sent to the renderer
18470 minZoom: 1e-50,
18471 maxZoom: 1e50,
18472 zoomingEnabled: defVal(true, options.zoomingEnabled),
18473 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18474 panningEnabled: defVal(true, options.panningEnabled),
18475 userPanningEnabled: defVal(true, options.userPanningEnabled),
18476 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18477 autolock: defVal(false, options.autolock, options.autolockNodes),
18478 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18479 autounselectify: defVal(false, options.autounselectify),
18480 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18481 zoom: number(options.zoom) ? options.zoom : 1,
18482 pan: {
18483 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
18484 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
18485 },
18486 animation: {
18487 // object for currently-running animations
18488 current: [],
18489 queue: []
18490 },
18491 hasCompoundNodes: false
18492 };
18493
18494 this.createEmitter(); // set selection type
18495
18496 this.selectionType(options.selectionType); // init zoom bounds
18497
18498 this.zoomRange({
18499 min: options.minZoom,
18500 max: options.maxZoom
18501 });
18502
18503 var loadExtData = function loadExtData(extData, next) {
18504 var anyIsPromise = extData.some(promise);
18505
18506 if (anyIsPromise) {
18507 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18508 } else {
18509 next(extData); // exec synchronously for convenience
18510 }
18511 }; // start with the default stylesheet so we have something before loading an external stylesheet
18512
18513
18514 if (_p.styleEnabled) {
18515 cy.setStyle([]);
18516 } // create the renderer
18517
18518
18519 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18520
18521 cy.initRenderer(rendererOptions);
18522
18523 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18524 cy.notifications(false); // remove old elements
18525
18526 var oldEles = cy.mutableElements();
18527
18528 if (oldEles.length > 0) {
18529 oldEles.remove();
18530 }
18531
18532 if (elements != null) {
18533 if (plainObject(elements) || array(elements)) {
18534 cy.add(elements);
18535 }
18536 }
18537
18538 cy.one('layoutready', function (e) {
18539 cy.notifications(true);
18540 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18541
18542 cy.one('load', onload);
18543 cy.emitAndNotify('load');
18544 }).one('layoutstop', function () {
18545 cy.one('done', ondone);
18546 cy.emit('done');
18547 });
18548 var layoutOpts = extend({}, cy._private.options.layout);
18549 layoutOpts.eles = cy.elements();
18550 cy.layout(layoutOpts).run();
18551 };
18552
18553 loadExtData([options.style, options.elements], function (thens) {
18554 var initStyle = thens[0];
18555 var initEles = thens[1]; // init style
18556
18557 if (_p.styleEnabled) {
18558 cy.style().append(initStyle);
18559 } // initial load
18560
18561
18562 setElesAndLayout(initEles, function () {
18563 // onready
18564 cy.startAnimationLoop();
18565 _p.ready = true; // if a ready callback is specified as an option, the bind it
18566
18567 if (fn(options.ready)) {
18568 cy.on('ready', options.ready);
18569 } // bind all the ready handlers registered before creating this instance
18570
18571
18572 for (var i = 0; i < readies.length; i++) {
18573 var fn$1 = readies[i];
18574 cy.on('ready', fn$1);
18575 }
18576
18577 if (reg) {
18578 reg.readies = [];
18579 } // 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
18580
18581
18582 cy.emit('ready');
18583 }, options.done);
18584 });
18585};
18586
18587var corefn$9 = Core.prototype; // short alias
18588
18589extend(corefn$9, {
18590 instanceString: function instanceString() {
18591 return 'core';
18592 },
18593 isReady: function isReady() {
18594 return this._private.ready;
18595 },
18596 destroyed: function destroyed() {
18597 return this._private.destroyed;
18598 },
18599 ready: function ready(fn) {
18600 if (this.isReady()) {
18601 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18602 } else {
18603 this.on('ready', fn);
18604 }
18605
18606 return this;
18607 },
18608 destroy: function destroy() {
18609 var cy = this;
18610 if (cy.destroyed()) return;
18611 cy.stopAnimationLoop();
18612 cy.destroyRenderer();
18613 this.emit('destroy');
18614 cy._private.destroyed = true;
18615 return cy;
18616 },
18617 hasElementWithId: function hasElementWithId(id) {
18618 return this._private.elements.hasElementWithId(id);
18619 },
18620 getElementById: function getElementById(id) {
18621 return this._private.elements.getElementById(id);
18622 },
18623 hasCompoundNodes: function hasCompoundNodes() {
18624 return this._private.hasCompoundNodes;
18625 },
18626 headless: function headless() {
18627 return this._private.renderer.isHeadless();
18628 },
18629 styleEnabled: function styleEnabled() {
18630 return this._private.styleEnabled;
18631 },
18632 addToPool: function addToPool(eles) {
18633 this._private.elements.merge(eles);
18634
18635 return this; // chaining
18636 },
18637 removeFromPool: function removeFromPool(eles) {
18638 this._private.elements.unmerge(eles);
18639
18640 return this;
18641 },
18642 container: function container() {
18643 return this._private.container || null;
18644 },
18645 mount: function mount(container) {
18646 if (container == null) {
18647 return;
18648 }
18649
18650 var cy = this;
18651 var _p = cy._private;
18652 var options = _p.options;
18653
18654 if (!htmlElement(container) && htmlElement(container[0])) {
18655 container = container[0];
18656 }
18657
18658 cy.stopAnimationLoop();
18659 cy.destroyRenderer();
18660 _p.container = container;
18661 _p.styleEnabled = true;
18662 cy.invalidateSize();
18663 cy.initRenderer(extend({}, options, options.renderer, {
18664 // allow custom renderer name to be re-used, otherwise use canvas
18665 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
18666 }));
18667 cy.startAnimationLoop();
18668 cy.style(options.style);
18669 cy.emit('mount');
18670 return cy;
18671 },
18672 unmount: function unmount() {
18673 var cy = this;
18674 cy.stopAnimationLoop();
18675 cy.destroyRenderer();
18676 cy.initRenderer({
18677 name: 'null'
18678 });
18679 cy.emit('unmount');
18680 return cy;
18681 },
18682 options: function options() {
18683 return copy(this._private.options);
18684 },
18685 json: function json(obj) {
18686 var cy = this;
18687 var _p = cy._private;
18688 var eles = cy.mutableElements();
18689
18690 var getFreshRef = function getFreshRef(ele) {
18691 return cy.getElementById(ele.id());
18692 };
18693
18694 if (plainObject(obj)) {
18695 // set
18696 cy.startBatch();
18697
18698 if (obj.elements) {
18699 var idInJson = {};
18700
18701 var updateEles = function updateEles(jsons, gr) {
18702 var toAdd = [];
18703 var toMod = [];
18704
18705 for (var i = 0; i < jsons.length; i++) {
18706 var json = jsons[i];
18707 var id = '' + json.data.id; // id must be string
18708
18709 var ele = cy.getElementById(id);
18710 idInJson[id] = true;
18711
18712 if (ele.length !== 0) {
18713 // existing element should be updated
18714 toMod.push({
18715 ele: ele,
18716 json: json
18717 });
18718 } else {
18719 // otherwise should be added
18720 if (gr) {
18721 json.group = gr;
18722 toAdd.push(json);
18723 } else {
18724 toAdd.push(json);
18725 }
18726 }
18727 }
18728
18729 cy.add(toAdd);
18730
18731 for (var _i = 0; _i < toMod.length; _i++) {
18732 var _toMod$_i = toMod[_i],
18733 _ele = _toMod$_i.ele,
18734 _json = _toMod$_i.json;
18735
18736 _ele.json(_json);
18737 }
18738 };
18739
18740 if (array(obj.elements)) {
18741 // elements: []
18742 updateEles(obj.elements);
18743 } else {
18744 // elements: { nodes: [], edges: [] }
18745 var grs = ['nodes', 'edges'];
18746
18747 for (var i = 0; i < grs.length; i++) {
18748 var gr = grs[i];
18749 var elements = obj.elements[gr];
18750
18751 if (array(elements)) {
18752 updateEles(elements, gr);
18753 }
18754 }
18755 }
18756
18757 var parentsToRemove = cy.collection();
18758 eles.filter(function (ele) {
18759 return !idInJson[ele.id()];
18760 }).forEach(function (ele) {
18761 if (ele.isParent()) {
18762 parentsToRemove.merge(ele);
18763 } else {
18764 ele.remove();
18765 }
18766 }); // so that children are not removed w/parent
18767
18768 parentsToRemove.forEach(function (ele) {
18769 return ele.children().move({
18770 parent: null
18771 });
18772 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
18773
18774 parentsToRemove.forEach(function (ele) {
18775 return getFreshRef(ele).remove();
18776 });
18777 }
18778
18779 if (obj.style) {
18780 cy.style(obj.style);
18781 }
18782
18783 if (obj.zoom != null && obj.zoom !== _p.zoom) {
18784 cy.zoom(obj.zoom);
18785 }
18786
18787 if (obj.pan) {
18788 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
18789 cy.pan(obj.pan);
18790 }
18791 }
18792
18793 if (obj.data) {
18794 cy.data(obj.data);
18795 }
18796
18797 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
18798
18799 for (var _i2 = 0; _i2 < fields.length; _i2++) {
18800 var f = fields[_i2];
18801
18802 if (obj[f] != null) {
18803 cy[f](obj[f]);
18804 }
18805 }
18806
18807 cy.endBatch();
18808 return this; // chaining
18809 } else {
18810 // get
18811 var flat = !!obj;
18812 var json = {};
18813
18814 if (flat) {
18815 json.elements = this.elements().map(function (ele) {
18816 return ele.json();
18817 });
18818 } else {
18819 json.elements = {};
18820 eles.forEach(function (ele) {
18821 var group = ele.group();
18822
18823 if (!json.elements[group]) {
18824 json.elements[group] = [];
18825 }
18826
18827 json.elements[group].push(ele.json());
18828 });
18829 }
18830
18831 if (this._private.styleEnabled) {
18832 json.style = cy.style().json();
18833 }
18834
18835 json.data = copy(cy.data());
18836 var options = _p.options;
18837 json.zoomingEnabled = _p.zoomingEnabled;
18838 json.userZoomingEnabled = _p.userZoomingEnabled;
18839 json.zoom = _p.zoom;
18840 json.minZoom = _p.minZoom;
18841 json.maxZoom = _p.maxZoom;
18842 json.panningEnabled = _p.panningEnabled;
18843 json.userPanningEnabled = _p.userPanningEnabled;
18844 json.pan = copy(_p.pan);
18845 json.boxSelectionEnabled = _p.boxSelectionEnabled;
18846 json.renderer = copy(options.renderer);
18847 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
18848 json.textureOnViewport = options.textureOnViewport;
18849 json.wheelSensitivity = options.wheelSensitivity;
18850 json.motionBlur = options.motionBlur;
18851 return json;
18852 }
18853 }
18854});
18855corefn$9.$id = corefn$9.getElementById;
18856[corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
18857 extend(corefn$9, props);
18858});
18859
18860/* eslint-disable no-unused-vars */
18861
18862var defaults$9 = {
18863 fit: true,
18864 // whether to fit the viewport to the graph
18865 directed: false,
18866 // whether the tree is directed downwards (or edges can point in any direction if false)
18867 padding: 30,
18868 // padding on fit
18869 circle: false,
18870 // put depths in concentric circles if true, put depths top down if false
18871 grid: false,
18872 // whether to create an even grid into which the DAG is placed (circle:false only)
18873 spacingFactor: 1.75,
18874 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
18875 boundingBox: undefined,
18876 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
18877 avoidOverlap: true,
18878 // prevents node overlap, may overflow boundingBox if not enough space
18879 nodeDimensionsIncludeLabels: false,
18880 // Excludes the label when calculating node bounding boxes for the layout algorithm
18881 roots: undefined,
18882 // the roots of the trees
18883 maximal: false,
18884 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
18885 animate: false,
18886 // whether to transition the node positions
18887 animationDuration: 500,
18888 // duration of animation in ms if enabled
18889 animationEasing: undefined,
18890 // easing of animation if enabled,
18891 animateFilter: function animateFilter(node, i) {
18892 return true;
18893 },
18894 // 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
18895 ready: undefined,
18896 // callback on layoutready
18897 stop: undefined,
18898 // callback on layoutstop
18899 transform: function transform(node, position) {
18900 return position;
18901 } // transform a given node position. Useful for changing flow direction in discrete layouts
18902
18903};
18904/* eslint-enable */
18905
18906var getInfo = function getInfo(ele) {
18907 return ele.scratch('breadthfirst');
18908};
18909
18910var setInfo = function setInfo(ele, obj) {
18911 return ele.scratch('breadthfirst', obj);
18912};
18913
18914function BreadthFirstLayout(options) {
18915 this.options = extend({}, defaults$9, options);
18916}
18917
18918BreadthFirstLayout.prototype.run = function () {
18919 var params = this.options;
18920 var options = params;
18921 var cy = params.cy;
18922 var eles = options.eles;
18923 var nodes = eles.nodes().filter(function (n) {
18924 return !n.isParent();
18925 });
18926 var graph = eles;
18927 var directed = options.directed;
18928 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
18929
18930 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
18931 x1: 0,
18932 y1: 0,
18933 w: cy.width(),
18934 h: cy.height()
18935 });
18936 var roots;
18937
18938 if (elementOrCollection(options.roots)) {
18939 roots = options.roots;
18940 } else if (array(options.roots)) {
18941 var rootsArray = [];
18942
18943 for (var i = 0; i < options.roots.length; i++) {
18944 var id = options.roots[i];
18945 var ele = cy.getElementById(id);
18946 rootsArray.push(ele);
18947 }
18948
18949 roots = cy.collection(rootsArray);
18950 } else if (string(options.roots)) {
18951 roots = cy.$(options.roots);
18952 } else {
18953 if (directed) {
18954 roots = nodes.roots();
18955 } else {
18956 var components = eles.components();
18957 roots = cy.collection();
18958
18959 var _loop = function _loop(_i) {
18960 var comp = components[_i];
18961 var maxDegree = comp.maxDegree(false);
18962 var compRoots = comp.filter(function (ele) {
18963 return ele.degree(false) === maxDegree;
18964 });
18965 roots = roots.add(compRoots);
18966 };
18967
18968 for (var _i = 0; _i < components.length; _i++) {
18969 _loop(_i);
18970 }
18971 }
18972 }
18973
18974 var depths = [];
18975 var foundByBfs = {};
18976
18977 var addToDepth = function addToDepth(ele, d) {
18978 if (depths[d] == null) {
18979 depths[d] = [];
18980 }
18981
18982 var i = depths[d].length;
18983 depths[d].push(ele);
18984 setInfo(ele, {
18985 index: i,
18986 depth: d
18987 });
18988 };
18989
18990 var changeDepth = function changeDepth(ele, newDepth) {
18991 var _getInfo = getInfo(ele),
18992 depth = _getInfo.depth,
18993 index = _getInfo.index;
18994
18995 depths[depth][index] = null;
18996 addToDepth(ele, newDepth);
18997 }; // find the depths of the nodes
18998
18999
19000 graph.bfs({
19001 roots: roots,
19002 directed: options.directed,
19003 visit: function visit(node, edge, pNode, i, depth) {
19004 var ele = node[0];
19005 var id = ele.id();
19006 addToDepth(ele, depth);
19007 foundByBfs[id] = true;
19008 }
19009 }); // check for nodes not found by bfs
19010
19011 var orphanNodes = [];
19012
19013 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19014 var _ele = nodes[_i2];
19015
19016 if (foundByBfs[_ele.id()]) {
19017 continue;
19018 } else {
19019 orphanNodes.push(_ele);
19020 }
19021 } // assign the nodes a depth and index
19022
19023
19024 var assignDepthsAt = function assignDepthsAt(i) {
19025 var eles = depths[i];
19026
19027 for (var j = 0; j < eles.length; j++) {
19028 var _ele2 = eles[j];
19029
19030 if (_ele2 == null) {
19031 eles.splice(j, 1);
19032 j--;
19033 continue;
19034 }
19035
19036 setInfo(_ele2, {
19037 depth: i,
19038 index: j
19039 });
19040 }
19041 };
19042
19043 var assignDepths = function assignDepths() {
19044 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19045 assignDepthsAt(_i3);
19046 }
19047 };
19048
19049 var adjustMaximally = function adjustMaximally(ele, shifted) {
19050 var eInfo = getInfo(ele);
19051 var incomers = ele.incomers().filter(function (el) {
19052 return el.isNode() && eles.has(el);
19053 });
19054 var maxDepth = -1;
19055 var id = ele.id();
19056
19057 for (var k = 0; k < incomers.length; k++) {
19058 var incmr = incomers[k];
19059 var iInfo = getInfo(incmr);
19060 maxDepth = Math.max(maxDepth, iInfo.depth);
19061 }
19062
19063 if (eInfo.depth <= maxDepth) {
19064 if (shifted[id]) {
19065 return null;
19066 }
19067
19068 changeDepth(ele, maxDepth + 1);
19069 shifted[id] = true;
19070 return true;
19071 }
19072
19073 return false;
19074 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19075
19076
19077 if (directed && maximal) {
19078 var Q = [];
19079 var shifted = {};
19080
19081 var enqueue = function enqueue(n) {
19082 return Q.push(n);
19083 };
19084
19085 var dequeue = function dequeue() {
19086 return Q.shift();
19087 };
19088
19089 nodes.forEach(function (n) {
19090 return Q.push(n);
19091 });
19092
19093 while (Q.length > 0) {
19094 var _ele3 = dequeue();
19095
19096 var didShift = adjustMaximally(_ele3, shifted);
19097
19098 if (didShift) {
19099 _ele3.outgoers().filter(function (el) {
19100 return el.isNode() && eles.has(el);
19101 }).forEach(enqueue);
19102 } else if (didShift === null) {
19103 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19104 break; // exit on failure
19105 }
19106 }
19107 }
19108
19109 assignDepths(); // clear holes
19110 // find min distance we need to leave between nodes
19111
19112 var minDistance = 0;
19113
19114 if (options.avoidOverlap) {
19115 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19116 var n = nodes[_i4];
19117 var nbb = n.layoutDimensions(options);
19118 var w = nbb.w;
19119 var h = nbb.h;
19120 minDistance = Math.max(minDistance, w, h);
19121 }
19122 } // get the weighted percent for an element based on its connectivity to other levels
19123
19124
19125 var cachedWeightedPercent = {};
19126
19127 var getWeightedPercent = function getWeightedPercent(ele) {
19128 if (cachedWeightedPercent[ele.id()]) {
19129 return cachedWeightedPercent[ele.id()];
19130 }
19131
19132 var eleDepth = getInfo(ele).depth;
19133 var neighbors = ele.neighborhood();
19134 var percent = 0;
19135 var samples = 0;
19136
19137 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19138 var neighbor = neighbors[_i5];
19139
19140 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19141 continue;
19142 }
19143
19144 var bf = getInfo(neighbor);
19145 var index = bf.index;
19146 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19147
19148 if (index == null || depth == null) {
19149 continue;
19150 }
19151
19152 var nDepth = depths[depth].length;
19153
19154 if (depth < eleDepth) {
19155 // only get influenced by elements above
19156 percent += index / nDepth;
19157 samples++;
19158 }
19159 }
19160
19161 samples = Math.max(1, samples);
19162 percent = percent / samples;
19163
19164 if (samples === 0) {
19165 // put lone nodes at the start
19166 percent = 0;
19167 }
19168
19169 cachedWeightedPercent[ele.id()] = percent;
19170 return percent;
19171 }; // rearrange the indices in each depth level based on connectivity
19172
19173
19174 var sortFn = function sortFn(a, b) {
19175 var apct = getWeightedPercent(a);
19176 var bpct = getWeightedPercent(b);
19177 var diff = apct - bpct;
19178
19179 if (diff === 0) {
19180 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19181 } else {
19182 return diff;
19183 }
19184 }; // sort each level to make connected nodes closer
19185
19186
19187 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19188 depths[_i6].sort(sortFn);
19189
19190 assignDepthsAt(_i6);
19191 } // assign orphan nodes to a new top-level depth
19192
19193
19194 var orphanDepth = [];
19195
19196 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19197 orphanDepth.push(orphanNodes[_i7]);
19198 }
19199
19200 depths.unshift(orphanDepth);
19201 assignDepths();
19202 var biggestDepthSize = 0;
19203
19204 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19205 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19206 }
19207
19208 var center = {
19209 x: bb.x1 + bb.w / 2,
19210 y: bb.x1 + bb.h / 2
19211 };
19212 var maxDepthSize = depths.reduce(function (max, eles) {
19213 return Math.max(max, eles.length);
19214 }, 0);
19215
19216 var getPosition = function getPosition(ele) {
19217 var _getInfo2 = getInfo(ele),
19218 depth = _getInfo2.depth,
19219 index = _getInfo2.index;
19220
19221 var depthSize = depths[depth].length;
19222 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19223 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19224 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19225 radiusStepSize = Math.max(radiusStepSize, minDistance);
19226
19227 if (!options.circle) {
19228 var epos = {
19229 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19230 y: (depth + 1) * distanceY
19231 };
19232 return epos;
19233 } else {
19234 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19235 var theta = 2 * Math.PI / depths[depth].length * index;
19236
19237 if (depth === 0 && depths[0].length === 1) {
19238 radius = 1;
19239 }
19240
19241 return {
19242 x: center.x + radius * Math.cos(theta),
19243 y: center.y + radius * Math.sin(theta)
19244 };
19245 }
19246 };
19247
19248 nodes.layoutPositions(this, options, getPosition);
19249 return this; // chaining
19250};
19251
19252var defaults$a = {
19253 fit: true,
19254 // whether to fit the viewport to the graph
19255 padding: 30,
19256 // the padding on fit
19257 boundingBox: undefined,
19258 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19259 avoidOverlap: true,
19260 // prevents node overlap, may overflow boundingBox and radius if not enough space
19261 nodeDimensionsIncludeLabels: false,
19262 // Excludes the label when calculating node bounding boxes for the layout algorithm
19263 spacingFactor: undefined,
19264 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19265 radius: undefined,
19266 // the radius of the circle
19267 startAngle: 3 / 2 * Math.PI,
19268 // where nodes start in radians
19269 sweep: undefined,
19270 // how many radians should be between the first and last node (defaults to full circle)
19271 clockwise: true,
19272 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19273 sort: undefined,
19274 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19275 animate: false,
19276 // whether to transition the node positions
19277 animationDuration: 500,
19278 // duration of animation in ms if enabled
19279 animationEasing: undefined,
19280 // easing of animation if enabled
19281 animateFilter: function animateFilter(node, i) {
19282 return true;
19283 },
19284 // 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
19285 ready: undefined,
19286 // callback on layoutready
19287 stop: undefined,
19288 // callback on layoutstop
19289 transform: function transform(node, position) {
19290 return position;
19291 } // transform a given node position. Useful for changing flow direction in discrete layouts
19292
19293};
19294
19295function CircleLayout(options) {
19296 this.options = extend({}, defaults$a, options);
19297}
19298
19299CircleLayout.prototype.run = function () {
19300 var params = this.options;
19301 var options = params;
19302 var cy = params.cy;
19303 var eles = options.eles;
19304 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19305 var nodes = eles.nodes().not(':parent');
19306
19307 if (options.sort) {
19308 nodes = nodes.sort(options.sort);
19309 }
19310
19311 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19312 x1: 0,
19313 y1: 0,
19314 w: cy.width(),
19315 h: cy.height()
19316 });
19317 var center = {
19318 x: bb.x1 + bb.w / 2,
19319 y: bb.y1 + bb.h / 2
19320 };
19321 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19322 var dTheta = sweep / Math.max(1, nodes.length - 1);
19323 var r;
19324 var minDistance = 0;
19325
19326 for (var i = 0; i < nodes.length; i++) {
19327 var n = nodes[i];
19328 var nbb = n.layoutDimensions(options);
19329 var w = nbb.w;
19330 var h = nbb.h;
19331 minDistance = Math.max(minDistance, w, h);
19332 }
19333
19334 if (number(options.radius)) {
19335 r = options.radius;
19336 } else if (nodes.length <= 1) {
19337 r = 0;
19338 } else {
19339 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19340 } // calculate the radius
19341
19342
19343 if (nodes.length > 1 && options.avoidOverlap) {
19344 // but only if more than one node (can't overlap)
19345 minDistance *= 1.75; // just to have some nice spacing
19346
19347 var dcos = Math.cos(dTheta) - Math.cos(0);
19348 var dsin = Math.sin(dTheta) - Math.sin(0);
19349 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19350
19351 r = Math.max(rMin, r);
19352 }
19353
19354 var getPos = function getPos(ele, i) {
19355 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19356 var rx = r * Math.cos(theta);
19357 var ry = r * Math.sin(theta);
19358 var pos = {
19359 x: center.x + rx,
19360 y: center.y + ry
19361 };
19362 return pos;
19363 };
19364
19365 nodes.layoutPositions(this, options, getPos);
19366 return this; // chaining
19367};
19368
19369var defaults$b = {
19370 fit: true,
19371 // whether to fit the viewport to the graph
19372 padding: 30,
19373 // the padding on fit
19374 startAngle: 3 / 2 * Math.PI,
19375 // where nodes start in radians
19376 sweep: undefined,
19377 // how many radians should be between the first and last node (defaults to full circle)
19378 clockwise: true,
19379 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19380 equidistant: false,
19381 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19382 minNodeSpacing: 10,
19383 // min spacing between outside of nodes (used for radius adjustment)
19384 boundingBox: undefined,
19385 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19386 avoidOverlap: true,
19387 // prevents node overlap, may overflow boundingBox if not enough space
19388 nodeDimensionsIncludeLabels: false,
19389 // Excludes the label when calculating node bounding boxes for the layout algorithm
19390 height: undefined,
19391 // height of layout area (overrides container height)
19392 width: undefined,
19393 // width of layout area (overrides container width)
19394 spacingFactor: undefined,
19395 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19396 concentric: function concentric(node) {
19397 // returns numeric value for each node, placing higher nodes in levels towards the centre
19398 return node.degree();
19399 },
19400 levelWidth: function levelWidth(nodes) {
19401 // the letiation of concentric values in each level
19402 return nodes.maxDegree() / 4;
19403 },
19404 animate: false,
19405 // whether to transition the node positions
19406 animationDuration: 500,
19407 // duration of animation in ms if enabled
19408 animationEasing: undefined,
19409 // easing of animation if enabled
19410 animateFilter: function animateFilter(node, i) {
19411 return true;
19412 },
19413 // 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
19414 ready: undefined,
19415 // callback on layoutready
19416 stop: undefined,
19417 // callback on layoutstop
19418 transform: function transform(node, position) {
19419 return position;
19420 } // transform a given node position. Useful for changing flow direction in discrete layouts
19421
19422};
19423
19424function ConcentricLayout(options) {
19425 this.options = extend({}, defaults$b, options);
19426}
19427
19428ConcentricLayout.prototype.run = function () {
19429 var params = this.options;
19430 var options = params;
19431 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19432 var cy = params.cy;
19433 var eles = options.eles;
19434 var nodes = eles.nodes().not(':parent');
19435 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19436 x1: 0,
19437 y1: 0,
19438 w: cy.width(),
19439 h: cy.height()
19440 });
19441 var center = {
19442 x: bb.x1 + bb.w / 2,
19443 y: bb.y1 + bb.h / 2
19444 };
19445 var nodeValues = []; // { node, value }
19446
19447 var maxNodeSize = 0;
19448
19449 for (var i = 0; i < nodes.length; i++) {
19450 var node = nodes[i];
19451 var value = void 0; // calculate the node value
19452
19453 value = options.concentric(node);
19454 nodeValues.push({
19455 value: value,
19456 node: node
19457 }); // for style mapping
19458
19459 node._private.scratch.concentric = value;
19460 } // in case we used the `concentric` in style
19461
19462
19463 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
19464
19465 for (var _i = 0; _i < nodes.length; _i++) {
19466 var _node = nodes[_i];
19467
19468 var nbb = _node.layoutDimensions(options);
19469
19470 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19471 } // sort node values in descreasing order
19472
19473
19474 nodeValues.sort(function (a, b) {
19475 return b.value - a.value;
19476 });
19477 var levelWidth = options.levelWidth(nodes); // put the values into levels
19478
19479 var levels = [[]];
19480 var currentLevel = levels[0];
19481
19482 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19483 var val = nodeValues[_i2];
19484
19485 if (currentLevel.length > 0) {
19486 var diff = Math.abs(currentLevel[0].value - val.value);
19487
19488 if (diff >= levelWidth) {
19489 currentLevel = [];
19490 levels.push(currentLevel);
19491 }
19492 }
19493
19494 currentLevel.push(val);
19495 } // create positions from levels
19496
19497
19498 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19499
19500 if (!options.avoidOverlap) {
19501 // then strictly constrain to bb
19502 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19503 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19504 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19505 minDist = Math.min(minDist, rStep);
19506 } // find the metrics for each level
19507
19508
19509 var r = 0;
19510
19511 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19512 var level = levels[_i3];
19513 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19514 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
19515
19516 if (level.length > 1 && options.avoidOverlap) {
19517 // but only if more than one node (can't overlap)
19518 var dcos = Math.cos(dTheta) - Math.cos(0);
19519 var dsin = Math.sin(dTheta) - Math.sin(0);
19520 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19521
19522 r = Math.max(rMin, r);
19523 }
19524
19525 level.r = r;
19526 r += minDist;
19527 }
19528
19529 if (options.equidistant) {
19530 var rDeltaMax = 0;
19531 var _r = 0;
19532
19533 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19534 var _level = levels[_i4];
19535 var rDelta = _level.r - _r;
19536 rDeltaMax = Math.max(rDeltaMax, rDelta);
19537 }
19538
19539 _r = 0;
19540
19541 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19542 var _level2 = levels[_i5];
19543
19544 if (_i5 === 0) {
19545 _r = _level2.r;
19546 }
19547
19548 _level2.r = _r;
19549 _r += rDeltaMax;
19550 }
19551 } // calculate the node positions
19552
19553
19554 var pos = {}; // id => position
19555
19556 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19557 var _level3 = levels[_i6];
19558 var _dTheta = _level3.dTheta;
19559 var _r2 = _level3.r;
19560
19561 for (var j = 0; j < _level3.length; j++) {
19562 var _val = _level3[j];
19563 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19564 var p = {
19565 x: center.x + _r2 * Math.cos(theta),
19566 y: center.y + _r2 * Math.sin(theta)
19567 };
19568 pos[_val.node.id()] = p;
19569 }
19570 } // position the nodes
19571
19572
19573 nodes.layoutPositions(this, options, function (ele) {
19574 var id = ele.id();
19575 return pos[id];
19576 });
19577 return this; // chaining
19578};
19579
19580/*
19581The CoSE layout was written by Gerardo Huck.
19582https://www.linkedin.com/in/gerardohuck/
19583
19584Based on the following article:
19585http://dl.acm.org/citation.cfm?id=1498047
19586
19587Modifications tracked on Github.
19588*/
19589var DEBUG;
19590/**
19591 * @brief : default layout options
19592 */
19593
19594var defaults$c = {
19595 // Called on `layoutready`
19596 ready: function ready() {},
19597 // Called on `layoutstop`
19598 stop: function stop() {},
19599 // Whether to animate while running the layout
19600 // true : Animate continuously as the layout is running
19601 // false : Just show the end result
19602 // 'end' : Animate with the end result, from the initial positions to the end positions
19603 animate: true,
19604 // Easing of the animation for animate:'end'
19605 animationEasing: undefined,
19606 // The duration of the animation for animate:'end'
19607 animationDuration: undefined,
19608 // A function that determines whether the node should be animated
19609 // All nodes animated by default on animate enabled
19610 // Non-animated nodes are positioned immediately when the layout starts
19611 animateFilter: function animateFilter(node, i) {
19612 return true;
19613 },
19614 // The layout animates only after this many milliseconds for animate:true
19615 // (prevents flashing on fast runs)
19616 animationThreshold: 250,
19617 // Number of iterations between consecutive screen positions update
19618 refresh: 20,
19619 // Whether to fit the network view after when done
19620 fit: true,
19621 // Padding on fit
19622 padding: 30,
19623 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19624 boundingBox: undefined,
19625 // Excludes the label when calculating node bounding boxes for the layout algorithm
19626 nodeDimensionsIncludeLabels: false,
19627 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19628 randomize: false,
19629 // Extra spacing between components in non-compound graphs
19630 componentSpacing: 40,
19631 // Node repulsion (non overlapping) multiplier
19632 nodeRepulsion: function nodeRepulsion(node) {
19633 return 2048;
19634 },
19635 // Node repulsion (overlapping) multiplier
19636 nodeOverlap: 4,
19637 // Ideal edge (non nested) length
19638 idealEdgeLength: function idealEdgeLength(edge) {
19639 return 32;
19640 },
19641 // Divisor to compute edge forces
19642 edgeElasticity: function edgeElasticity(edge) {
19643 return 32;
19644 },
19645 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19646 nestingFactor: 1.2,
19647 // Gravity force (constant)
19648 gravity: 1,
19649 // Maximum number of iterations to perform
19650 numIter: 1000,
19651 // Initial temperature (maximum node displacement)
19652 initialTemp: 1000,
19653 // Cooling factor (how the temperature is reduced between consecutive iterations
19654 coolingFactor: 0.99,
19655 // Lower temperature threshold (below this point the layout will end)
19656 minTemp: 1.0
19657};
19658/**
19659 * @brief : constructor
19660 * @arg options : object containing layout options
19661 */
19662
19663function CoseLayout(options) {
19664 this.options = extend({}, defaults$c, options);
19665 this.options.layout = this;
19666}
19667/**
19668 * @brief : runs the layout
19669 */
19670
19671
19672CoseLayout.prototype.run = function () {
19673 var options = this.options;
19674 var cy = options.cy;
19675 var layout = this;
19676 layout.stopped = false;
19677
19678 if (options.animate === true || options.animate === false) {
19679 layout.emit({
19680 type: 'layoutstart',
19681 layout: layout
19682 });
19683 } // Set DEBUG - Global variable
19684
19685
19686 if (true === options.debug) {
19687 DEBUG = true;
19688 } else {
19689 DEBUG = false;
19690 } // Initialize layout info
19691
19692
19693 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
19694
19695 if (DEBUG) {
19696 printLayoutInfo(layoutInfo);
19697 } // If required, randomize node positions
19698
19699
19700 if (options.randomize) {
19701 randomizePositions(layoutInfo);
19702 }
19703
19704 var startTime = performanceNow();
19705
19706 var refresh = function refresh() {
19707 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
19708
19709 if (true === options.fit) {
19710 cy.fit(options.padding);
19711 }
19712 };
19713
19714 var mainLoop = function mainLoop(i) {
19715 if (layout.stopped || i >= options.numIter) {
19716 // logDebug("Layout manually stopped. Stopping computation in step " + i);
19717 return false;
19718 } // Do one step in the phisical simulation
19719
19720
19721 step$1(layoutInfo, options); // Update temperature
19722
19723 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
19724
19725 if (layoutInfo.temperature < options.minTemp) {
19726 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
19727 return false;
19728 }
19729
19730 return true;
19731 };
19732
19733 var done = function done() {
19734 if (options.animate === true || options.animate === false) {
19735 refresh(); // Layout has finished
19736
19737 layout.one('layoutstop', options.stop);
19738 layout.emit({
19739 type: 'layoutstop',
19740 layout: layout
19741 });
19742 } else {
19743 var nodes = options.eles.nodes();
19744 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19745 nodes.layoutPositions(layout, options, getScaledPos);
19746 }
19747 };
19748
19749 var i = 0;
19750 var loopRet = true;
19751
19752 if (options.animate === true) {
19753 var frame = function frame() {
19754 var f = 0;
19755
19756 while (loopRet && f < options.refresh) {
19757 loopRet = mainLoop(i);
19758 i++;
19759 f++;
19760 }
19761
19762 if (!loopRet) {
19763 // it's done
19764 separateComponents(layoutInfo, options);
19765 done();
19766 } else {
19767 var now = performanceNow();
19768
19769 if (now - startTime >= options.animationThreshold) {
19770 refresh();
19771 }
19772
19773 requestAnimationFrame(frame);
19774 }
19775 };
19776
19777 frame();
19778 } else {
19779 while (loopRet) {
19780 loopRet = mainLoop(i);
19781 i++;
19782 }
19783
19784 separateComponents(layoutInfo, options);
19785 done();
19786 }
19787
19788 return this; // chaining
19789};
19790/**
19791 * @brief : called on continuous layouts to stop them before they finish
19792 */
19793
19794
19795CoseLayout.prototype.stop = function () {
19796 this.stopped = true;
19797
19798 if (this.thread) {
19799 this.thread.stop();
19800 }
19801
19802 this.emit('layoutstop');
19803 return this; // chaining
19804};
19805
19806CoseLayout.prototype.destroy = function () {
19807 if (this.thread) {
19808 this.thread.stop();
19809 }
19810
19811 return this; // chaining
19812};
19813/**
19814 * @brief : Creates an object which is contains all the data
19815 * used in the layout process
19816 * @arg cy : cytoscape.js object
19817 * @return : layoutInfo object initialized
19818 */
19819
19820
19821var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
19822 // Shortcut
19823 var edges = options.eles.edges();
19824 var nodes = options.eles.nodes();
19825 var layoutInfo = {
19826 isCompound: cy.hasCompoundNodes(),
19827 layoutNodes: [],
19828 idToIndex: {},
19829 nodeSize: nodes.size(),
19830 graphSet: [],
19831 indexToGraph: [],
19832 layoutEdges: [],
19833 edgeSize: edges.size(),
19834 temperature: options.initialTemp,
19835 clientWidth: cy.width(),
19836 clientHeight: cy.width(),
19837 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
19838 x1: 0,
19839 y1: 0,
19840 w: cy.width(),
19841 h: cy.height()
19842 })
19843 };
19844 var components = options.eles.components();
19845 var id2cmptId = {};
19846
19847 for (var i = 0; i < components.length; i++) {
19848 var component = components[i];
19849
19850 for (var j = 0; j < component.length; j++) {
19851 var node = component[j];
19852 id2cmptId[node.id()] = i;
19853 }
19854 } // Iterate over all nodes, creating layout nodes
19855
19856
19857 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19858 var n = nodes[i];
19859 var nbb = n.layoutDimensions(options);
19860 var tempNode = {};
19861 tempNode.isLocked = n.locked();
19862 tempNode.id = n.data('id');
19863 tempNode.parentId = n.data('parent');
19864 tempNode.cmptId = id2cmptId[n.id()];
19865 tempNode.children = [];
19866 tempNode.positionX = n.position('x');
19867 tempNode.positionY = n.position('y');
19868 tempNode.offsetX = 0;
19869 tempNode.offsetY = 0;
19870 tempNode.height = nbb.w;
19871 tempNode.width = nbb.h;
19872 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
19873 tempNode.minX = tempNode.positionX - tempNode.width / 2;
19874 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
19875 tempNode.minY = tempNode.positionY - tempNode.height / 2;
19876 tempNode.padLeft = parseFloat(n.style('padding'));
19877 tempNode.padRight = parseFloat(n.style('padding'));
19878 tempNode.padTop = parseFloat(n.style('padding'));
19879 tempNode.padBottom = parseFloat(n.style('padding')); // forces
19880
19881 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
19882
19883 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
19884
19885 layoutInfo.idToIndex[tempNode.id] = i;
19886 } // Inline implementation of a queue, used for traversing the graph in BFS order
19887
19888
19889 var queue = [];
19890 var start = 0; // Points to the start the queue
19891
19892 var end = -1; // Points to the end of the queue
19893
19894 var tempGraph = []; // Second pass to add child information and
19895 // initialize queue for hierarchical traversal
19896
19897 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19898 var n = layoutInfo.layoutNodes[i];
19899 var p_id = n.parentId; // Check if node n has a parent node
19900
19901 if (null != p_id) {
19902 // Add node Id to parent's list of children
19903 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
19904 } else {
19905 // If a node doesn't have a parent, then it's in the root graph
19906 queue[++end] = n.id;
19907 tempGraph.push(n.id);
19908 }
19909 } // Add root graph to graphSet
19910
19911
19912 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
19913
19914 while (start <= end) {
19915 // Get the node to visit and remove it from queue
19916 var node_id = queue[start++];
19917 var node_ix = layoutInfo.idToIndex[node_id];
19918 var node = layoutInfo.layoutNodes[node_ix];
19919 var children = node.children;
19920
19921 if (children.length > 0) {
19922 // Add children nodes as a new graph to graph set
19923 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
19924
19925 for (var i = 0; i < children.length; i++) {
19926 queue[++end] = children[i];
19927 }
19928 }
19929 } // Create indexToGraph map
19930
19931
19932 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
19933 var graph = layoutInfo.graphSet[i];
19934
19935 for (var j = 0; j < graph.length; j++) {
19936 var index = layoutInfo.idToIndex[graph[j]];
19937 layoutInfo.indexToGraph[index] = i;
19938 }
19939 } // Iterate over all edges, creating Layout Edges
19940
19941
19942 for (var i = 0; i < layoutInfo.edgeSize; i++) {
19943 var e = edges[i];
19944 var tempEdge = {};
19945 tempEdge.id = e.data('id');
19946 tempEdge.sourceId = e.data('source');
19947 tempEdge.targetId = e.data('target'); // Compute ideal length
19948
19949 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
19950 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
19951
19952 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
19953 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
19954 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
19955 var targetGraph = layoutInfo.indexToGraph[targetIx];
19956
19957 if (sourceGraph != targetGraph) {
19958 // Find lowest common graph ancestor
19959 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
19960
19961 var lcaGraph = layoutInfo.graphSet[lca];
19962 var depth = 0; // Source depth
19963
19964 var tempNode = layoutInfo.layoutNodes[sourceIx];
19965
19966 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19967 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19968 depth++;
19969 } // Target depth
19970
19971
19972 tempNode = layoutInfo.layoutNodes[targetIx];
19973
19974 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19975 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19976 depth++;
19977 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
19978 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
19979 // ". Depth: " + depth);
19980 // Update idealLength
19981
19982
19983 idealLength *= depth * options.nestingFactor;
19984 }
19985
19986 tempEdge.idealLength = idealLength;
19987 tempEdge.elasticity = elasticity;
19988 layoutInfo.layoutEdges.push(tempEdge);
19989 } // Finally, return layoutInfo object
19990
19991
19992 return layoutInfo;
19993};
19994/**
19995 * @brief : This function finds the index of the lowest common
19996 * graph ancestor between 2 nodes in the subtree
19997 * (from the graph hierarchy induced tree) whose
19998 * root is graphIx
19999 *
20000 * @arg node1: node1's ID
20001 * @arg node2: node2's ID
20002 * @arg layoutInfo: layoutInfo object
20003 *
20004 */
20005
20006
20007var findLCA = function findLCA(node1, node2, layoutInfo) {
20008 // Find their common ancester, starting from the root graph
20009 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20010
20011 if (2 > res.count) {
20012 // If aux function couldn't find the common ancester,
20013 // then it is the root graph
20014 return 0;
20015 } else {
20016 return res.graph;
20017 }
20018};
20019/**
20020 * @brief : Auxiliary function used for LCA computation
20021 *
20022 * @arg node1 : node1's ID
20023 * @arg node2 : node2's ID
20024 * @arg graphIx : subgraph index
20025 * @arg layoutInfo : layoutInfo object
20026 *
20027 * @return : object of the form {count: X, graph: Y}, where:
20028 * X is the number of ancesters (max: 2) found in
20029 * graphIx (and it's subgraphs),
20030 * Y is the graph index of the lowest graph containing
20031 * all X nodes
20032 */
20033
20034
20035var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20036 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20037
20038 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20039 return {
20040 count: 2,
20041 graph: graphIx
20042 };
20043 } // Make recursive calls for all subgraphs
20044
20045
20046 var c = 0;
20047
20048 for (var i = 0; i < graph.length; i++) {
20049 var nodeId = graph[i];
20050 var nodeIx = layoutInfo.idToIndex[nodeId];
20051 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20052
20053 if (0 === children.length) {
20054 continue;
20055 }
20056
20057 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20058 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20059
20060 if (0 === result.count) {
20061 // Neither node1 nor node2 are present in this subgraph
20062 continue;
20063 } else if (1 === result.count) {
20064 // One of (node1, node2) is present in this subgraph
20065 c++;
20066
20067 if (2 === c) {
20068 // We've already found both nodes, no need to keep searching
20069 break;
20070 }
20071 } else {
20072 // Both nodes are present in this subgraph
20073 return result;
20074 }
20075 }
20076
20077 return {
20078 count: c,
20079 graph: graphIx
20080 };
20081};
20082/**
20083 * @brief: printsLayoutInfo into js console
20084 * Only used for debbuging
20085 */
20086
20087
20088if (false) {
20089 var printLayoutInfo;
20090}
20091/**
20092 * @brief : Randomizes the position of all nodes
20093 */
20094
20095
20096var randomizePositions = function randomizePositions(layoutInfo, cy) {
20097 var width = layoutInfo.clientWidth;
20098 var height = layoutInfo.clientHeight;
20099
20100 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20101 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20102
20103 if (0 === n.children.length && !n.isLocked) {
20104 n.positionX = Math.random() * width;
20105 n.positionY = Math.random() * height;
20106 }
20107 }
20108};
20109
20110var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20111 var bb = layoutInfo.boundingBox;
20112 var coseBB = {
20113 x1: Infinity,
20114 x2: -Infinity,
20115 y1: Infinity,
20116 y2: -Infinity
20117 };
20118
20119 if (options.boundingBox) {
20120 nodes.forEach(function (node) {
20121 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20122 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20123 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20124 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20125 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20126 });
20127 coseBB.w = coseBB.x2 - coseBB.x1;
20128 coseBB.h = coseBB.y2 - coseBB.y1;
20129 }
20130
20131 return function (ele, i) {
20132 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20133
20134 if (options.boundingBox) {
20135 // then add extra bounding box constraint
20136 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20137 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20138 return {
20139 x: bb.x1 + pctX * bb.w,
20140 y: bb.y1 + pctY * bb.h
20141 };
20142 } else {
20143 return {
20144 x: lnode.positionX,
20145 y: lnode.positionY
20146 };
20147 }
20148 };
20149};
20150/**
20151 * @brief : Updates the positions of nodes in the network
20152 * @arg layoutInfo : LayoutInfo object
20153 * @arg cy : Cytoscape object
20154 * @arg options : Layout options
20155 */
20156
20157
20158var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20159 // var s = 'Refreshing positions';
20160 // logDebug(s);
20161 var layout = options.layout;
20162 var nodes = options.eles.nodes();
20163 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20164 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20165
20166 if (true !== layoutInfo.ready) {
20167 // s = 'Triggering layoutready';
20168 // logDebug(s);
20169 layoutInfo.ready = true;
20170 layout.one('layoutready', options.ready);
20171 layout.emit({
20172 type: 'layoutready',
20173 layout: this
20174 });
20175 }
20176};
20177/**
20178 * @brief : Logs a debug message in JS console, if DEBUG is ON
20179 */
20180// var logDebug = function(text) {
20181// if (DEBUG) {
20182// console.debug(text);
20183// }
20184// };
20185
20186/**
20187 * @brief : Performs one iteration of the physical simulation
20188 * @arg layoutInfo : LayoutInfo object already initialized
20189 * @arg cy : Cytoscape object
20190 * @arg options : Layout options
20191 */
20192
20193
20194var step$1 = function step(layoutInfo, options, _step) {
20195 // var s = "\n\n###############################";
20196 // s += "\nSTEP: " + step;
20197 // s += "\n###############################\n";
20198 // logDebug(s);
20199 // Calculate node repulsions
20200 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20201
20202 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20203
20204 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20205
20206 propagateForces(layoutInfo); // Update positions based on calculated forces
20207
20208 updatePositions(layoutInfo);
20209};
20210/**
20211 * @brief : Computes the node repulsion forces
20212 */
20213
20214
20215var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20216 // Go through each of the graphs in graphSet
20217 // Nodes only repel each other if they belong to the same graph
20218 // var s = 'calculateNodeForces';
20219 // logDebug(s);
20220 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20221 var graph = layoutInfo.graphSet[i];
20222 var numNodes = graph.length; // s = "Set: " + graph.toString();
20223 // logDebug(s);
20224 // Now get all the pairs of nodes
20225 // Only get each pair once, (A, B) = (B, A)
20226
20227 for (var j = 0; j < numNodes; j++) {
20228 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20229
20230 for (var k = j + 1; k < numNodes; k++) {
20231 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20232 nodeRepulsion(node1, node2, layoutInfo, options);
20233 }
20234 }
20235 }
20236};
20237
20238var randomDistance = function randomDistance(max) {
20239 return -max + 2 * max * Math.random();
20240};
20241/**
20242 * @brief : Compute the node repulsion forces between a pair of nodes
20243 */
20244
20245
20246var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20247 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20248 var cmptId1 = node1.cmptId;
20249 var cmptId2 = node2.cmptId;
20250
20251 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20252 return;
20253 } // Get direction of line connecting both node centers
20254
20255
20256 var directionX = node2.positionX - node1.positionX;
20257 var directionY = node2.positionY - node1.positionY;
20258 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20259 // If both centers are the same, apply a random force
20260
20261 if (0 === directionX && 0 === directionY) {
20262 directionX = randomDistance(maxRandDist);
20263 directionY = randomDistance(maxRandDist);
20264 }
20265
20266 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20267
20268 if (overlap > 0) {
20269 // s += "\nNodes DO overlap.";
20270 // s += "\nOverlap: " + overlap;
20271 // If nodes overlap, repulsion force is proportional
20272 // to the overlap
20273 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
20274
20275 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
20276
20277 var forceX = force * directionX / distance;
20278 var forceY = force * directionY / distance;
20279 } else {
20280 // s += "\nNodes do NOT overlap.";
20281 // If there's no overlap, force is inversely proportional
20282 // to squared distance
20283 // Get clipping points for both nodes
20284 var point1 = findClippingPoint(node1, directionX, directionY);
20285 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
20286
20287 var distanceX = point2.x - point1.x;
20288 var distanceY = point2.y - point1.y;
20289 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20290 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
20291 // Compute the module and components of the force vector
20292
20293 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20294 var forceX = force * distanceX / distance;
20295 var forceY = force * distanceY / distance;
20296 } // Apply force
20297
20298
20299 if (!node1.isLocked) {
20300 node1.offsetX -= forceX;
20301 node1.offsetY -= forceY;
20302 }
20303
20304 if (!node2.isLocked) {
20305 node2.offsetX += forceX;
20306 node2.offsetY += forceY;
20307 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20308 // logDebug(s);
20309
20310
20311 return;
20312};
20313/**
20314 * @brief : Determines whether two nodes overlap or not
20315 * @return : Amount of overlapping (0 => no overlap)
20316 */
20317
20318
20319var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20320 if (dX > 0) {
20321 var overlapX = node1.maxX - node2.minX;
20322 } else {
20323 var overlapX = node2.maxX - node1.minX;
20324 }
20325
20326 if (dY > 0) {
20327 var overlapY = node1.maxY - node2.minY;
20328 } else {
20329 var overlapY = node2.maxY - node1.minY;
20330 }
20331
20332 if (overlapX >= 0 && overlapY >= 0) {
20333 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20334 } else {
20335 return 0;
20336 }
20337};
20338/**
20339 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20340 * the rectangular bounding box of it's source/target node
20341 */
20342
20343
20344var findClippingPoint = function findClippingPoint(node, dX, dY) {
20345 // Shorcuts
20346 var X = node.positionX;
20347 var Y = node.positionY;
20348 var H = node.height || 1;
20349 var W = node.width || 1;
20350 var dirSlope = dY / dX;
20351 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
20352 // " . Height: " + H + ", Width: " + W +
20353 // "\nDirection " + dX + ", " + dY;
20354 //
20355 // Compute intersection
20356
20357 var res = {}; // Case: Vertical direction (up)
20358
20359 if (0 === dX && 0 < dY) {
20360 res.x = X; // s += "\nUp direction";
20361
20362 res.y = Y + H / 2;
20363 return res;
20364 } // Case: Vertical direction (down)
20365
20366
20367 if (0 === dX && 0 > dY) {
20368 res.x = X;
20369 res.y = Y + H / 2; // s += "\nDown direction";
20370
20371 return res;
20372 } // Case: Intersects the right border
20373
20374
20375 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20376 res.x = X + W / 2;
20377 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
20378
20379 return res;
20380 } // Case: Intersects the left border
20381
20382
20383 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20384 res.x = X - W / 2;
20385 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
20386
20387 return res;
20388 } // Case: Intersects the top border
20389
20390
20391 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20392 res.x = X + H * dX / 2 / dY;
20393 res.y = Y + H / 2; // s += "\nTop border";
20394
20395 return res;
20396 } // Case: Intersects the bottom border
20397
20398
20399 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20400 res.x = X - H * dX / 2 / dY;
20401 res.y = Y - H / 2; // s += "\nBottom border";
20402
20403 return res;
20404 } // s += "\nClipping point found at " + res.x + ", " + res.y;
20405 // logDebug(s);
20406
20407
20408 return res;
20409};
20410/**
20411 * @brief : Calculates all edge forces
20412 */
20413
20414
20415var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20416 // Iterate over all edges
20417 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20418 // Get edge, source & target nodes
20419 var edge = layoutInfo.layoutEdges[i];
20420 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20421 var source = layoutInfo.layoutNodes[sourceIx];
20422 var targetIx = layoutInfo.idToIndex[edge.targetId];
20423 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
20424
20425 var directionX = target.positionX - source.positionX;
20426 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
20427 // A random force has already been applied as node repulsion
20428
20429 if (0 === directionX && 0 === directionY) {
20430 continue;
20431 } // Get clipping points for both nodes
20432
20433
20434 var point1 = findClippingPoint(source, directionX, directionY);
20435 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20436 var lx = point2.x - point1.x;
20437 var ly = point2.y - point1.y;
20438 var l = Math.sqrt(lx * lx + ly * ly);
20439 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20440
20441 if (0 !== l) {
20442 var forceX = force * lx / l;
20443 var forceY = force * ly / l;
20444 } else {
20445 var forceX = 0;
20446 var forceY = 0;
20447 } // Add this force to target and source nodes
20448
20449
20450 if (!source.isLocked) {
20451 source.offsetX += forceX;
20452 source.offsetY += forceY;
20453 }
20454
20455 if (!target.isLocked) {
20456 target.offsetX -= forceX;
20457 target.offsetY -= forceY;
20458 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20459 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20460 // logDebug(s);
20461
20462 }
20463};
20464/**
20465 * @brief : Computes gravity forces for all nodes
20466 */
20467
20468
20469var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20470 var distThreshold = 1; // var s = 'calculateGravityForces';
20471 // logDebug(s);
20472
20473 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20474 var graph = layoutInfo.graphSet[i];
20475 var numNodes = graph.length; // s = "Set: " + graph.toString();
20476 // logDebug(s);
20477 // Compute graph center
20478
20479 if (0 === i) {
20480 var centerX = layoutInfo.clientHeight / 2;
20481 var centerY = layoutInfo.clientWidth / 2;
20482 } else {
20483 // Get Parent node for this graph, and use its position as center
20484 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20485 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20486 var centerX = parent.positionX;
20487 var centerY = parent.positionY;
20488 } // s = "Center found at: " + centerX + ", " + centerY;
20489 // logDebug(s);
20490 // Apply force to all nodes in graph
20491
20492
20493 for (var j = 0; j < numNodes; j++) {
20494 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
20495
20496 if (node.isLocked) {
20497 continue;
20498 }
20499
20500 var dx = centerX - node.positionX;
20501 var dy = centerY - node.positionY;
20502 var d = Math.sqrt(dx * dx + dy * dy);
20503
20504 if (d > distThreshold) {
20505 var fx = options.gravity * dx / d;
20506 var fy = options.gravity * dy / d;
20507 node.offsetX += fx;
20508 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
20509 } // s += ": skypped since it's too close to center";
20510 // logDebug(s);
20511
20512 }
20513 }
20514};
20515/**
20516 * @brief : This function propagates the existing offsets from
20517 * parent nodes to its descendents.
20518 * @arg layoutInfo : layoutInfo Object
20519 * @arg cy : cytoscape Object
20520 * @arg options : Layout options
20521 */
20522
20523
20524var propagateForces = function propagateForces(layoutInfo, options) {
20525 // Inline implementation of a queue, used for traversing the graph in BFS order
20526 var queue = [];
20527 var start = 0; // Points to the start the queue
20528
20529 var end = -1; // Points to the end of the queue
20530 // logDebug('propagateForces');
20531 // Start by visiting the nodes in the root graph
20532
20533 queue.push.apply(queue, layoutInfo.graphSet[0]);
20534 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
20535
20536 while (start <= end) {
20537 // Get the node to visit and remove it from queue
20538 var nodeId = queue[start++];
20539 var nodeIndex = layoutInfo.idToIndex[nodeId];
20540 var node = layoutInfo.layoutNodes[nodeIndex];
20541 var children = node.children; // We only need to process the node if it's compound
20542
20543 if (0 < children.length && !node.isLocked) {
20544 var offX = node.offsetX;
20545 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
20546 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20547 // s += "\n Children: " + children.toString();
20548 // logDebug(s);
20549
20550 for (var i = 0; i < children.length; i++) {
20551 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
20552
20553 childNode.offsetX += offX;
20554 childNode.offsetY += offY; // Add children to queue to be visited
20555
20556 queue[++end] = children[i];
20557 } // Reset parent offsets
20558
20559
20560 node.offsetX = 0;
20561 node.offsetY = 0;
20562 }
20563 }
20564};
20565/**
20566 * @brief : Updates the layout model positions, based on
20567 * the accumulated forces
20568 */
20569
20570
20571var updatePositions = function updatePositions(layoutInfo, options) {
20572 // var s = 'Updating positions';
20573 // logDebug(s);
20574 // Reset boundaries for compound nodes
20575 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20576 var n = layoutInfo.layoutNodes[i];
20577
20578 if (0 < n.children.length) {
20579 // logDebug("Resetting boundaries of compound node: " + n.id);
20580 n.maxX = undefined;
20581 n.minX = undefined;
20582 n.maxY = undefined;
20583 n.minY = undefined;
20584 }
20585 }
20586
20587 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20588 var n = layoutInfo.layoutNodes[i];
20589
20590 if (0 < n.children.length || n.isLocked) {
20591 // No need to set compound or locked node position
20592 // logDebug("Skipping position update of node: " + n.id);
20593 continue;
20594 } // s = "Node: " + n.id + " Previous position: (" +
20595 // n.positionX + ", " + n.positionY + ").";
20596 // Limit displacement in order to improve stability
20597
20598
20599 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20600 n.positionX += tempForce.x;
20601 n.positionY += tempForce.y;
20602 n.offsetX = 0;
20603 n.offsetY = 0;
20604 n.minX = n.positionX - n.width;
20605 n.maxX = n.positionX + n.width;
20606 n.minY = n.positionY - n.height;
20607 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20608 // logDebug(s);
20609 // Update ancestry boudaries
20610
20611 updateAncestryBoundaries(n, layoutInfo);
20612 } // Update size, position of compund nodes
20613
20614
20615 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20616 var n = layoutInfo.layoutNodes[i];
20617
20618 if (0 < n.children.length && !n.isLocked) {
20619 n.positionX = (n.maxX + n.minX) / 2;
20620 n.positionY = (n.maxY + n.minY) / 2;
20621 n.width = n.maxX - n.minX;
20622 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
20623 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20624 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20625 // logDebug(s);
20626 }
20627 }
20628};
20629/**
20630 * @brief : Limits a force (forceX, forceY) to be not
20631 * greater (in modulo) than max.
20632 8 Preserves force direction.
20633 */
20634
20635
20636var limitForce = function limitForce(forceX, forceY, max) {
20637 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20638 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20639
20640 if (force > max) {
20641 var res = {
20642 x: max * forceX / force,
20643 y: max * forceY / force
20644 };
20645 } else {
20646 var res = {
20647 x: forceX,
20648 y: forceY
20649 };
20650 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20651 // logDebug(s);
20652
20653
20654 return res;
20655};
20656/**
20657 * @brief : Function used for keeping track of compound node
20658 * sizes, since they should bound all their subnodes.
20659 */
20660
20661
20662var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20663 // var s = "Propagating new position/size of node " + node.id;
20664 var parentId = node.parentId;
20665
20666 if (null == parentId) {
20667 // If there's no parent, we are done
20668 // s += ". No parent node.";
20669 // logDebug(s);
20670 return;
20671 } // Get Parent Node
20672
20673
20674 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20675 var flag = false; // MaxX
20676
20677 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20678 p.maxX = node.maxX + p.padRight;
20679 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20680 } // MinX
20681
20682
20683 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20684 p.minX = node.minX - p.padLeft;
20685 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20686 } // MaxY
20687
20688
20689 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20690 p.maxY = node.maxY + p.padBottom;
20691 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20692 } // MinY
20693
20694
20695 if (null == p.minY || node.minY - p.padTop < p.minY) {
20696 p.minY = node.minY - p.padTop;
20697 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20698 } // If updated boundaries, propagate changes upward
20699
20700
20701 if (flag) {
20702 // logDebug(s);
20703 return updateAncestryBoundaries(p, layoutInfo);
20704 } // s += ". No changes in boundaries/position of parent node " + p.id;
20705 // logDebug(s);
20706
20707
20708 return;
20709};
20710
20711var separateComponents = function separateComponents(layoutInfo, options) {
20712 var nodes = layoutInfo.layoutNodes;
20713 var components = [];
20714
20715 for (var i = 0; i < nodes.length; i++) {
20716 var node = nodes[i];
20717 var cid = node.cmptId;
20718 var component = components[cid] = components[cid] || [];
20719 component.push(node);
20720 }
20721
20722 var totalA = 0;
20723
20724 for (var i = 0; i < components.length; i++) {
20725 var c = components[i];
20726
20727 if (!c) {
20728 continue;
20729 }
20730
20731 c.x1 = Infinity;
20732 c.x2 = -Infinity;
20733 c.y1 = Infinity;
20734 c.y2 = -Infinity;
20735
20736 for (var j = 0; j < c.length; j++) {
20737 var n = c[j];
20738 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
20739 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
20740 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
20741 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
20742 }
20743
20744 c.w = c.x2 - c.x1;
20745 c.h = c.y2 - c.y1;
20746 totalA += c.w * c.h;
20747 }
20748
20749 components.sort(function (c1, c2) {
20750 return c2.w * c2.h - c1.w * c1.h;
20751 });
20752 var x = 0;
20753 var y = 0;
20754 var usedW = 0;
20755 var rowH = 0;
20756 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
20757
20758 for (var i = 0; i < components.length; i++) {
20759 var c = components[i];
20760
20761 if (!c) {
20762 continue;
20763 }
20764
20765 for (var j = 0; j < c.length; j++) {
20766 var n = c[j];
20767
20768 if (!n.isLocked) {
20769 n.positionX += x - c.x1;
20770 n.positionY += y - c.y1;
20771 }
20772 }
20773
20774 x += c.w + options.componentSpacing;
20775 usedW += c.w + options.componentSpacing;
20776 rowH = Math.max(rowH, c.h);
20777
20778 if (usedW > maxRowW) {
20779 y += rowH + options.componentSpacing;
20780 x = 0;
20781 usedW = 0;
20782 rowH = 0;
20783 }
20784 }
20785};
20786
20787var defaults$d = {
20788 fit: true,
20789 // whether to fit the viewport to the graph
20790 padding: 30,
20791 // padding used on fit
20792 boundingBox: undefined,
20793 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20794 avoidOverlap: true,
20795 // prevents node overlap, may overflow boundingBox if not enough space
20796 avoidOverlapPadding: 10,
20797 // extra spacing around nodes when avoidOverlap: true
20798 nodeDimensionsIncludeLabels: false,
20799 // Excludes the label when calculating node bounding boxes for the layout algorithm
20800 spacingFactor: undefined,
20801 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20802 condense: false,
20803 // uses all available space on false, uses minimal space on true
20804 rows: undefined,
20805 // force num of rows in the grid
20806 cols: undefined,
20807 // force num of columns in the grid
20808 position: function position(node) {},
20809 // returns { row, col } for element
20810 sort: undefined,
20811 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20812 animate: false,
20813 // whether to transition the node positions
20814 animationDuration: 500,
20815 // duration of animation in ms if enabled
20816 animationEasing: undefined,
20817 // easing of animation if enabled
20818 animateFilter: function animateFilter(node, i) {
20819 return true;
20820 },
20821 // 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
20822 ready: undefined,
20823 // callback on layoutready
20824 stop: undefined,
20825 // callback on layoutstop
20826 transform: function transform(node, position) {
20827 return position;
20828 } // transform a given node position. Useful for changing flow direction in discrete layouts
20829
20830};
20831
20832function GridLayout(options) {
20833 this.options = extend({}, defaults$d, options);
20834}
20835
20836GridLayout.prototype.run = function () {
20837 var params = this.options;
20838 var options = params;
20839 var cy = params.cy;
20840 var eles = options.eles;
20841 var nodes = eles.nodes().not(':parent');
20842
20843 if (options.sort) {
20844 nodes = nodes.sort(options.sort);
20845 }
20846
20847 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20848 x1: 0,
20849 y1: 0,
20850 w: cy.width(),
20851 h: cy.height()
20852 });
20853
20854 if (bb.h === 0 || bb.w === 0) {
20855 nodes.layoutPositions(this, options, function (ele) {
20856 return {
20857 x: bb.x1,
20858 y: bb.y1
20859 };
20860 });
20861 } else {
20862 // width/height * splits^2 = cells where splits is number of times to split width
20863 var cells = nodes.size();
20864 var splits = Math.sqrt(cells * bb.h / bb.w);
20865 var rows = Math.round(splits);
20866 var cols = Math.round(bb.w / bb.h * splits);
20867
20868 var small = function small(val) {
20869 if (val == null) {
20870 return Math.min(rows, cols);
20871 } else {
20872 var min = Math.min(rows, cols);
20873
20874 if (min == rows) {
20875 rows = val;
20876 } else {
20877 cols = val;
20878 }
20879 }
20880 };
20881
20882 var large = function large(val) {
20883 if (val == null) {
20884 return Math.max(rows, cols);
20885 } else {
20886 var max = Math.max(rows, cols);
20887
20888 if (max == rows) {
20889 rows = val;
20890 } else {
20891 cols = val;
20892 }
20893 }
20894 };
20895
20896 var oRows = options.rows;
20897 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
20898
20899 if (oRows != null && oCols != null) {
20900 rows = oRows;
20901 cols = oCols;
20902 } else if (oRows != null && oCols == null) {
20903 rows = oRows;
20904 cols = Math.ceil(cells / rows);
20905 } else if (oRows == null && oCols != null) {
20906 cols = oCols;
20907 rows = Math.ceil(cells / cols);
20908 } // otherwise use the automatic values and adjust accordingly
20909 // if rounding was up, see if we can reduce rows or columns
20910 else if (cols * rows > cells) {
20911 var sm = small();
20912 var lg = large(); // reducing the small side takes away the most cells, so try it first
20913
20914 if ((sm - 1) * lg >= cells) {
20915 small(sm - 1);
20916 } else if ((lg - 1) * sm >= cells) {
20917 large(lg - 1);
20918 }
20919 } else {
20920 // if rounding was too low, add rows or columns
20921 while (cols * rows < cells) {
20922 var _sm = small();
20923
20924 var _lg = large(); // try to add to larger side first (adds less in multiplication)
20925
20926
20927 if ((_lg + 1) * _sm >= cells) {
20928 large(_lg + 1);
20929 } else {
20930 small(_sm + 1);
20931 }
20932 }
20933 }
20934
20935 var cellWidth = bb.w / cols;
20936 var cellHeight = bb.h / rows;
20937
20938 if (options.condense) {
20939 cellWidth = 0;
20940 cellHeight = 0;
20941 }
20942
20943 if (options.avoidOverlap) {
20944 for (var i = 0; i < nodes.length; i++) {
20945 var node = nodes[i];
20946 var pos = node._private.position;
20947
20948 if (pos.x == null || pos.y == null) {
20949 // for bb
20950 pos.x = 0;
20951 pos.y = 0;
20952 }
20953
20954 var nbb = node.layoutDimensions(options);
20955 var p = options.avoidOverlapPadding;
20956 var w = nbb.w + p;
20957 var h = nbb.h + p;
20958 cellWidth = Math.max(cellWidth, w);
20959 cellHeight = Math.max(cellHeight, h);
20960 }
20961 }
20962
20963 var cellUsed = {}; // e.g. 'c-0-2' => true
20964
20965 var used = function used(row, col) {
20966 return cellUsed['c-' + row + '-' + col] ? true : false;
20967 };
20968
20969 var use = function use(row, col) {
20970 cellUsed['c-' + row + '-' + col] = true;
20971 }; // to keep track of current cell position
20972
20973
20974 var row = 0;
20975 var col = 0;
20976
20977 var moveToNextCell = function moveToNextCell() {
20978 col++;
20979
20980 if (col >= cols) {
20981 col = 0;
20982 row++;
20983 }
20984 }; // get a cache of all the manual positions
20985
20986
20987 var id2manPos = {};
20988
20989 for (var _i = 0; _i < nodes.length; _i++) {
20990 var _node = nodes[_i];
20991 var rcPos = options.position(_node);
20992
20993 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
20994 // must have at least row or col def'd
20995 var _pos = {
20996 row: rcPos.row,
20997 col: rcPos.col
20998 };
20999
21000 if (_pos.col === undefined) {
21001 // find unused col
21002 _pos.col = 0;
21003
21004 while (used(_pos.row, _pos.col)) {
21005 _pos.col++;
21006 }
21007 } else if (_pos.row === undefined) {
21008 // find unused row
21009 _pos.row = 0;
21010
21011 while (used(_pos.row, _pos.col)) {
21012 _pos.row++;
21013 }
21014 }
21015
21016 id2manPos[_node.id()] = _pos;
21017 use(_pos.row, _pos.col);
21018 }
21019 }
21020
21021 var getPos = function getPos(element, i) {
21022 var x, y;
21023
21024 if (element.locked() || element.isParent()) {
21025 return false;
21026 } // see if we have a manual position set
21027
21028
21029 var rcPos = id2manPos[element.id()];
21030
21031 if (rcPos) {
21032 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21033 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21034 } else {
21035 // otherwise set automatically
21036 while (used(row, col)) {
21037 moveToNextCell();
21038 }
21039
21040 x = col * cellWidth + cellWidth / 2 + bb.x1;
21041 y = row * cellHeight + cellHeight / 2 + bb.y1;
21042 use(row, col);
21043 moveToNextCell();
21044 }
21045
21046 return {
21047 x: x,
21048 y: y
21049 };
21050 };
21051
21052 nodes.layoutPositions(this, options, getPos);
21053 }
21054
21055 return this; // chaining
21056};
21057
21058var defaults$e = {
21059 ready: function ready() {},
21060 // on layoutready
21061 stop: function stop() {} // on layoutstop
21062
21063}; // constructor
21064// options : object containing layout options
21065
21066function NullLayout(options) {
21067 this.options = extend({}, defaults$e, options);
21068} // runs the layout
21069
21070
21071NullLayout.prototype.run = function () {
21072 var options = this.options;
21073 var eles = options.eles; // elements to consider in the layout
21074
21075 var layout = this; // cy is automatically populated for us in the constructor
21076 // (disable eslint for next line as this serves as example layout code to external developers)
21077 // eslint-disable-next-line no-unused-vars
21078
21079 var cy = options.cy;
21080 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21081 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21082
21083 eles.nodes().positions(function () {
21084 return {
21085 x: 0,
21086 y: 0
21087 };
21088 }); // trigger layoutready when each node has had its position set at least once
21089
21090 layout.one('layoutready', options.ready);
21091 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21092
21093 layout.one('layoutstop', options.stop);
21094 layout.emit('layoutstop');
21095 return this; // chaining
21096}; // called on continuous layouts to stop them before they finish
21097
21098
21099NullLayout.prototype.stop = function () {
21100 return this; // chaining
21101};
21102
21103var defaults$f = {
21104 positions: undefined,
21105 // map of (node id) => (position obj); or function(node){ return somPos; }
21106 zoom: undefined,
21107 // the zoom level to set (prob want fit = false if set)
21108 pan: undefined,
21109 // the pan level to set (prob want fit = false if set)
21110 fit: true,
21111 // whether to fit to viewport
21112 padding: 30,
21113 // padding on fit
21114 animate: false,
21115 // whether to transition the node positions
21116 animationDuration: 500,
21117 // duration of animation in ms if enabled
21118 animationEasing: undefined,
21119 // easing of animation if enabled
21120 animateFilter: function animateFilter(node, i) {
21121 return true;
21122 },
21123 // 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
21124 ready: undefined,
21125 // callback on layoutready
21126 stop: undefined,
21127 // callback on layoutstop
21128 transform: function transform(node, position) {
21129 return position;
21130 } // transform a given node position. Useful for changing flow direction in discrete layouts
21131
21132};
21133
21134function PresetLayout(options) {
21135 this.options = extend({}, defaults$f, options);
21136}
21137
21138PresetLayout.prototype.run = function () {
21139 var options = this.options;
21140 var eles = options.eles;
21141 var nodes = eles.nodes();
21142 var posIsFn = fn(options.positions);
21143
21144 function getPosition(node) {
21145 if (options.positions == null) {
21146 return copyPosition(node.position());
21147 }
21148
21149 if (posIsFn) {
21150 return options.positions(node);
21151 }
21152
21153 var pos = options.positions[node._private.data.id];
21154
21155 if (pos == null) {
21156 return null;
21157 }
21158
21159 return pos;
21160 }
21161
21162 nodes.layoutPositions(this, options, function (node, i) {
21163 var position = getPosition(node);
21164
21165 if (node.locked() || position == null) {
21166 return false;
21167 }
21168
21169 return position;
21170 });
21171 return this; // chaining
21172};
21173
21174var defaults$g = {
21175 fit: true,
21176 // whether to fit to viewport
21177 padding: 30,
21178 // fit padding
21179 boundingBox: undefined,
21180 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21181 animate: false,
21182 // whether to transition the node positions
21183 animationDuration: 500,
21184 // duration of animation in ms if enabled
21185 animationEasing: undefined,
21186 // easing of animation if enabled
21187 animateFilter: function animateFilter(node, i) {
21188 return true;
21189 },
21190 // 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
21191 ready: undefined,
21192 // callback on layoutready
21193 stop: undefined,
21194 // callback on layoutstop
21195 transform: function transform(node, position) {
21196 return position;
21197 } // transform a given node position. Useful for changing flow direction in discrete layouts
21198
21199};
21200
21201function RandomLayout(options) {
21202 this.options = extend({}, defaults$g, options);
21203}
21204
21205RandomLayout.prototype.run = function () {
21206 var options = this.options;
21207 var cy = options.cy;
21208 var eles = options.eles;
21209 var nodes = eles.nodes().not(':parent');
21210 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21211 x1: 0,
21212 y1: 0,
21213 w: cy.width(),
21214 h: cy.height()
21215 });
21216
21217 var getPos = function getPos(node, i) {
21218 return {
21219 x: bb.x1 + Math.round(Math.random() * bb.w),
21220 y: bb.y1 + Math.round(Math.random() * bb.h)
21221 };
21222 };
21223
21224 nodes.layoutPositions(this, options, getPos);
21225 return this; // chaining
21226};
21227
21228var layout = [{
21229 name: 'breadthfirst',
21230 impl: BreadthFirstLayout
21231}, {
21232 name: 'circle',
21233 impl: CircleLayout
21234}, {
21235 name: 'concentric',
21236 impl: ConcentricLayout
21237}, {
21238 name: 'cose',
21239 impl: CoseLayout
21240}, {
21241 name: 'grid',
21242 impl: GridLayout
21243}, {
21244 name: 'null',
21245 impl: NullLayout
21246}, {
21247 name: 'preset',
21248 impl: PresetLayout
21249}, {
21250 name: 'random',
21251 impl: RandomLayout
21252}];
21253
21254function NullRenderer(options) {
21255 this.options = options;
21256 this.notifications = 0; // for testing
21257}
21258
21259var noop$1 = function noop() {};
21260
21261var throwImgErr = function throwImgErr() {
21262 throw new Error('A headless instance can not render images');
21263};
21264
21265NullRenderer.prototype = {
21266 recalculateRenderedStyle: noop$1,
21267 notify: function notify() {
21268 this.notifications++;
21269 },
21270 init: noop$1,
21271 isHeadless: function isHeadless() {
21272 return true;
21273 },
21274 png: throwImgErr,
21275 jpg: throwImgErr
21276};
21277
21278var BRp = {};
21279BRp.arrowShapeWidth = 0.3;
21280
21281BRp.registerArrowShapes = function () {
21282 var arrowShapes = this.arrowShapes = {};
21283 var renderer = this; // Contract for arrow shapes:
21284 // 0, 0 is arrow tip
21285 // (0, 1) is direction towards node
21286 // (1, 0) is right
21287 //
21288 // functional api:
21289 // collide: check x, y in shape
21290 // roughCollide: called before collide, no false negatives
21291 // draw: draw
21292 // spacing: dist(arrowTip, nodeBoundary)
21293 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21294
21295 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21296 var x1 = translation.x - size / 2 - padding;
21297 var x2 = translation.x + size / 2 + padding;
21298 var y1 = translation.y - size / 2 - padding;
21299 var y2 = translation.y + size / 2 + padding;
21300 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21301 return inside;
21302 };
21303
21304 var transform = function transform(x, y, size, angle, translation) {
21305 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21306 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21307 var xScaled = xRotated * size;
21308 var yScaled = yRotated * size;
21309 var xTranslated = xScaled + translation.x;
21310 var yTranslated = yScaled + translation.y;
21311 return {
21312 x: xTranslated,
21313 y: yTranslated
21314 };
21315 };
21316
21317 var transformPoints = function transformPoints(pts, size, angle, translation) {
21318 var retPts = [];
21319
21320 for (var i = 0; i < pts.length; i += 2) {
21321 var x = pts[i];
21322 var y = pts[i + 1];
21323 retPts.push(transform(x, y, size, angle, translation));
21324 }
21325
21326 return retPts;
21327 };
21328
21329 var pointsToArr = function pointsToArr(pts) {
21330 var ret = [];
21331
21332 for (var i = 0; i < pts.length; i++) {
21333 var p = pts[i];
21334 ret.push(p.x, p.y);
21335 }
21336
21337 return ret;
21338 };
21339
21340 var standardGap = function standardGap(edge) {
21341 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21342 };
21343
21344 var defineArrowShape = function defineArrowShape(name, defn) {
21345 if (string(defn)) {
21346 defn = arrowShapes[defn];
21347 }
21348
21349 arrowShapes[name] = extend({
21350 name: name,
21351 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21352 collide: function collide(x, y, size, angle, translation, padding) {
21353 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21354 var inside = pointInsidePolygonPoints(x, y, points);
21355 return inside;
21356 },
21357 roughCollide: bbCollide,
21358 draw: function draw(context, size, angle, translation) {
21359 var points = transformPoints(this.points, size, angle, translation);
21360 renderer.arrowShapeImpl('polygon')(context, points);
21361 },
21362 spacing: function spacing(edge) {
21363 return 0;
21364 },
21365 gap: standardGap
21366 }, defn);
21367 };
21368
21369 defineArrowShape('none', {
21370 collide: falsify,
21371 roughCollide: falsify,
21372 draw: noop,
21373 spacing: zeroify,
21374 gap: zeroify
21375 });
21376 defineArrowShape('triangle', {
21377 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21378 });
21379 defineArrowShape('arrow', 'triangle');
21380 defineArrowShape('triangle-backcurve', {
21381 points: arrowShapes['triangle'].points,
21382 controlPoint: [0, -0.15],
21383 roughCollide: bbCollide,
21384 draw: function draw(context, size, angle, translation, edgeWidth) {
21385 var ptsTrans = transformPoints(this.points, size, angle, translation);
21386 var ctrlPt = this.controlPoint;
21387 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21388 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21389 },
21390 gap: function gap(edge) {
21391 return standardGap(edge) * 0.8;
21392 }
21393 });
21394 defineArrowShape('triangle-tee', {
21395 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21396 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21397 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21398 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21399 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21400 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21401 return inside;
21402 },
21403 draw: function draw(context, size, angle, translation, edgeWidth) {
21404 var triPts = transformPoints(this.points, size, angle, translation);
21405 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21406 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21407 }
21408 });
21409 defineArrowShape('triangle-cross', {
21410 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21411 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
21412 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
21413 0.15, -0.4],
21414 crossLinePts: function crossLinePts(size, edgeWidth) {
21415 // shift points so that the distance between the cross points matches edge width
21416 var p = this.baseCrossLinePts.slice();
21417 var shiftFactor = edgeWidth / size;
21418 var y0 = 3;
21419 var y1 = 5;
21420 p[y0] = p[y0] - shiftFactor;
21421 p[y1] = p[y1] - shiftFactor;
21422 return p;
21423 },
21424 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21425 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21426 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21427 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21428 return inside;
21429 },
21430 draw: function draw(context, size, angle, translation, edgeWidth) {
21431 var triPts = transformPoints(this.points, size, angle, translation);
21432 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21433 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21434 }
21435 });
21436 defineArrowShape('vee', {
21437 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21438 gap: function gap(edge) {
21439 return standardGap(edge) * 0.525;
21440 }
21441 });
21442 defineArrowShape('circle', {
21443 radius: 0.15,
21444 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21445 var t = translation;
21446 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21447 return inside;
21448 },
21449 draw: function draw(context, size, angle, translation, edgeWidth) {
21450 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21451 },
21452 spacing: function spacing(edge) {
21453 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21454 }
21455 });
21456 defineArrowShape('tee', {
21457 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21458 spacing: function spacing(edge) {
21459 return 1;
21460 },
21461 gap: function gap(edge) {
21462 return 1;
21463 }
21464 });
21465 defineArrowShape('square', {
21466 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21467 });
21468 defineArrowShape('diamond', {
21469 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21470 gap: function gap(edge) {
21471 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21472 }
21473 });
21474 defineArrowShape('chevron', {
21475 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21476 gap: function gap(edge) {
21477 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21478 }
21479 });
21480};
21481
21482var BRp$1 = {}; // Project mouse
21483
21484BRp$1.projectIntoViewport = function (clientX, clientY) {
21485 var cy = this.cy;
21486 var offsets = this.findContainerClientCoords();
21487 var offsetLeft = offsets[0];
21488 var offsetTop = offsets[1];
21489 var scale = offsets[4];
21490 var pan = cy.pan();
21491 var zoom = cy.zoom();
21492 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21493 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21494 return [x, y];
21495};
21496
21497BRp$1.findContainerClientCoords = function () {
21498 if (this.containerBB) {
21499 return this.containerBB;
21500 }
21501
21502 var container = this.container;
21503 var rect = container.getBoundingClientRect();
21504 var style = window$1.getComputedStyle(container);
21505
21506 var styleValue = function styleValue(name) {
21507 return parseFloat(style.getPropertyValue(name));
21508 };
21509
21510 var padding = {
21511 left: styleValue('padding-left'),
21512 right: styleValue('padding-right'),
21513 top: styleValue('padding-top'),
21514 bottom: styleValue('padding-bottom')
21515 };
21516 var border = {
21517 left: styleValue('border-left-width'),
21518 right: styleValue('border-right-width'),
21519 top: styleValue('border-top-width'),
21520 bottom: styleValue('border-bottom-width')
21521 };
21522 var clientWidth = container.clientWidth;
21523 var clientHeight = container.clientHeight;
21524 var paddingHor = padding.left + padding.right;
21525 var paddingVer = padding.top + padding.bottom;
21526 var borderHor = border.left + border.right;
21527 var scale = rect.width / (clientWidth + borderHor);
21528 var unscaledW = clientWidth - paddingHor;
21529 var unscaledH = clientHeight - paddingVer;
21530 var left = rect.left + padding.left + border.left;
21531 var top = rect.top + padding.top + border.top;
21532 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21533};
21534
21535BRp$1.invalidateContainerClientCoordsCache = function () {
21536 this.containerBB = null;
21537};
21538
21539BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21540 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21541};
21542
21543BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21544 var self = this;
21545 var r = this;
21546 var eles = r.getCachedZSortedEles();
21547 var near = []; // 1 node max, 1 edge max
21548
21549 var zoom = r.cy.zoom();
21550 var hasCompounds = r.cy.hasCompoundNodes();
21551 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21552 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21553 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21554 var minSqDist = Infinity;
21555 var nearEdge;
21556 var nearNode;
21557
21558 if (interactiveElementsOnly) {
21559 eles = eles.interactive;
21560 }
21561
21562 function addEle(ele, sqDist) {
21563 if (ele.isNode()) {
21564 if (nearNode) {
21565 return; // can't replace node
21566 } else {
21567 nearNode = ele;
21568 near.push(ele);
21569 }
21570 }
21571
21572 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21573 if (nearEdge) {
21574 // then replace existing edge
21575 // can replace only if same z-index
21576 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) {
21577 for (var i = 0; i < near.length; i++) {
21578 if (near[i].isEdge()) {
21579 near[i] = ele;
21580 nearEdge = ele;
21581 minSqDist = sqDist != null ? sqDist : minSqDist;
21582 break;
21583 }
21584 }
21585 }
21586 } else {
21587 near.push(ele);
21588 nearEdge = ele;
21589 minSqDist = sqDist != null ? sqDist : minSqDist;
21590 }
21591 }
21592 }
21593
21594 function checkNode(node) {
21595 var width = node.outerWidth() + 2 * nodeThreshold;
21596 var height = node.outerHeight() + 2 * nodeThreshold;
21597 var hw = width / 2;
21598 var hh = height / 2;
21599 var pos = node.position();
21600
21601 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21602 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21603 ) {
21604 var shape = r.nodeShapes[self.getNodeShape(node)];
21605
21606 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
21607 addEle(node, 0);
21608 return true;
21609 }
21610 }
21611 }
21612
21613 function checkEdge(edge) {
21614 var _p = edge._private;
21615 var rs = _p.rscratch;
21616 var styleWidth = edge.pstyle('width').pfValue;
21617 var scale = edge.pstyle('arrow-scale').value;
21618 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21619
21620 var widthSq = width * width;
21621 var width2 = width * 2;
21622 var src = _p.source;
21623 var tgt = _p.target;
21624 var sqDist;
21625
21626 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21627 var pts = rs.allpts;
21628
21629 for (var i = 0; i + 3 < pts.length; i += 2) {
21630 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]))) {
21631 addEle(edge, sqDist);
21632 return true;
21633 }
21634 }
21635 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21636 var pts = rs.allpts;
21637
21638 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21639 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]))) {
21640 addEle(edge, sqDist);
21641 return true;
21642 }
21643 }
21644 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
21645
21646
21647 var src = src || _p.source;
21648 var tgt = tgt || _p.target;
21649 var arSize = self.getArrowWidth(styleWidth, scale);
21650 var arrows = [{
21651 name: 'source',
21652 x: rs.arrowStartX,
21653 y: rs.arrowStartY,
21654 angle: rs.srcArrowAngle
21655 }, {
21656 name: 'target',
21657 x: rs.arrowEndX,
21658 y: rs.arrowEndY,
21659 angle: rs.tgtArrowAngle
21660 }, {
21661 name: 'mid-source',
21662 x: rs.midX,
21663 y: rs.midY,
21664 angle: rs.midsrcArrowAngle
21665 }, {
21666 name: 'mid-target',
21667 x: rs.midX,
21668 y: rs.midY,
21669 angle: rs.midtgtArrowAngle
21670 }];
21671
21672 for (var i = 0; i < arrows.length; i++) {
21673 var ar = arrows[i];
21674 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21675 var edgeWidth = edge.pstyle('width').pfValue;
21676
21677 if (shape.roughCollide(x, y, arSize, ar.angle, {
21678 x: ar.x,
21679 y: ar.y
21680 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21681 x: ar.x,
21682 y: ar.y
21683 }, edgeWidth, edgeThreshold)) {
21684 addEle(edge);
21685 return true;
21686 }
21687 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21688
21689
21690 if (hasCompounds && near.length > 0) {
21691 checkNode(src);
21692 checkNode(tgt);
21693 }
21694 }
21695
21696 function preprop(obj, name, pre) {
21697 return getPrefixedProperty(obj, name, pre);
21698 }
21699
21700 function checkLabel(ele, prefix) {
21701 var _p = ele._private;
21702 var th = labelThreshold;
21703 var prefixDash;
21704
21705 if (prefix) {
21706 prefixDash = prefix + '-';
21707 } else {
21708 prefixDash = '';
21709 }
21710
21711 ele.boundingBox();
21712 var bb = _p.labelBounds[prefix || 'main'];
21713 var text = ele.pstyle(prefixDash + 'label').value;
21714 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21715
21716 if (!eventsEnabled || !text) {
21717 return;
21718 }
21719
21720 var rstyle = _p.rstyle;
21721 var lx = preprop(rstyle, 'labelX', prefix);
21722 var ly = preprop(rstyle, 'labelY', prefix);
21723 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21724 var lx1 = bb.x1 - th;
21725 var lx2 = bb.x2 + th;
21726 var ly1 = bb.y1 - th;
21727 var ly2 = bb.y2 + th;
21728
21729 if (theta) {
21730 var cos = Math.cos(theta);
21731 var sin = Math.sin(theta);
21732
21733 var rotate = function rotate(x, y) {
21734 x = x - lx;
21735 y = y - ly;
21736 return {
21737 x: x * cos - y * sin + lx,
21738 y: x * sin + y * cos + ly
21739 };
21740 };
21741
21742 var px1y1 = rotate(lx1, ly1);
21743 var px1y2 = rotate(lx1, ly2);
21744 var px2y1 = rotate(lx2, ly1);
21745 var px2y2 = rotate(lx2, ly2);
21746 var points = [px1y1.x, px1y1.y, px2y1.x, px2y1.y, px2y2.x, px2y2.y, px1y2.x, px1y2.y];
21747
21748 if (pointInsidePolygonPoints(x, y, points)) {
21749 addEle(ele);
21750 return true;
21751 }
21752 } else {
21753 // do a cheaper bb check
21754 if (inBoundingBox(bb, x, y)) {
21755 addEle(ele);
21756 return true;
21757 }
21758 }
21759 }
21760
21761 for (var i = eles.length - 1; i >= 0; i--) {
21762 // reverse order for precedence
21763 var ele = eles[i];
21764
21765 if (ele.isNode()) {
21766 checkNode(ele) || checkLabel(ele);
21767 } else {
21768 // then edge
21769 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
21770 }
21771 }
21772
21773 return near;
21774}; // 'Give me everything from this box'
21775
21776
21777BRp$1.getAllInBox = function (x1, y1, x2, y2) {
21778 var eles = this.getCachedZSortedEles().interactive;
21779 var box = [];
21780 var x1c = Math.min(x1, x2);
21781 var x2c = Math.max(x1, x2);
21782 var y1c = Math.min(y1, y2);
21783 var y2c = Math.max(y1, y2);
21784 x1 = x1c;
21785 x2 = x2c;
21786 y1 = y1c;
21787 y2 = y2c;
21788 var boxBb = makeBoundingBox({
21789 x1: x1,
21790 y1: y1,
21791 x2: x2,
21792 y2: y2
21793 });
21794
21795 for (var e = 0; e < eles.length; e++) {
21796 var ele = eles[e];
21797
21798 if (ele.isNode()) {
21799 var node = ele;
21800 var nodeBb = node.boundingBox({
21801 includeNodes: true,
21802 includeEdges: false,
21803 includeLabels: false
21804 });
21805
21806 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
21807 box.push(node);
21808 }
21809 } else {
21810 var edge = ele;
21811 var _p = edge._private;
21812 var rs = _p.rscratch;
21813
21814 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
21815 continue;
21816 }
21817
21818 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
21819 continue;
21820 }
21821
21822 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
21823 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
21824 var allInside = true;
21825
21826 for (var i = 0; i < pts.length; i++) {
21827 if (!pointInBoundingBox(boxBb, pts[i])) {
21828 allInside = false;
21829 break;
21830 }
21831 }
21832
21833 if (allInside) {
21834 box.push(edge);
21835 }
21836 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
21837 box.push(edge);
21838 }
21839 }
21840 }
21841
21842 return box;
21843};
21844
21845var BRp$2 = {};
21846
21847BRp$2.calculateArrowAngles = function (edge) {
21848 var rs = edge._private.rscratch;
21849 var isHaystack = rs.edgeType === 'haystack';
21850 var isBezier = rs.edgeType === 'bezier';
21851 var isMultibezier = rs.edgeType === 'multibezier';
21852 var isSegments = rs.edgeType === 'segments';
21853 var isCompound = rs.edgeType === 'compound';
21854 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
21855
21856 var dispX, dispY;
21857 var startX, startY, endX, endY, midX, midY;
21858
21859 if (isHaystack) {
21860 startX = rs.haystackPts[0];
21861 startY = rs.haystackPts[1];
21862 endX = rs.haystackPts[2];
21863 endY = rs.haystackPts[3];
21864 } else {
21865 startX = rs.arrowStartX;
21866 startY = rs.arrowStartY;
21867 endX = rs.arrowEndX;
21868 endY = rs.arrowEndY;
21869 }
21870
21871 midX = rs.midX;
21872 midY = rs.midY; // source
21873 //
21874
21875 if (isSegments) {
21876 dispX = startX - rs.segpts[0];
21877 dispY = startY - rs.segpts[1];
21878 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21879 var pts = rs.allpts;
21880 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
21881 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
21882 dispX = startX - bX;
21883 dispY = startY - bY;
21884 } else {
21885 dispX = startX - midX;
21886 dispY = startY - midY;
21887 }
21888
21889 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
21890 //
21891
21892 var midX = rs.midX;
21893 var midY = rs.midY;
21894
21895 if (isHaystack) {
21896 midX = (startX + endX) / 2;
21897 midY = (startY + endY) / 2;
21898 }
21899
21900 dispX = endX - startX;
21901 dispY = endY - startY;
21902
21903 if (isSegments) {
21904 var pts = rs.allpts;
21905
21906 if (pts.length / 2 % 2 === 0) {
21907 var i2 = pts.length / 2;
21908 var i1 = i2 - 2;
21909 dispX = pts[i2] - pts[i1];
21910 dispY = pts[i2 + 1] - pts[i1 + 1];
21911 } else {
21912 var i2 = pts.length / 2 - 1;
21913 var i1 = i2 - 2;
21914 var i3 = i2 + 2;
21915 dispX = pts[i2] - pts[i1];
21916 dispY = pts[i2 + 1] - pts[i1 + 1];
21917 }
21918 } else if (isMultibezier || isCompound || isSelf) {
21919 var pts = rs.allpts;
21920 var cpts = rs.ctrlpts;
21921 var bp0x, bp0y;
21922 var bp1x, bp1y;
21923
21924 if (cpts.length / 2 % 2 === 0) {
21925 var p0 = pts.length / 2 - 1; // startpt
21926
21927 var ic = p0 + 2;
21928 var p1 = ic + 2;
21929 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
21930 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
21931 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
21932 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
21933 } else {
21934 var ic = pts.length / 2 - 1; // ctrpt
21935
21936 var p0 = ic - 2; // startpt
21937
21938 var p1 = ic + 2; // endpt
21939
21940 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
21941 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
21942 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
21943 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
21944 }
21945
21946 dispX = bp1x - bp0x;
21947 dispY = bp1y - bp0y;
21948 }
21949
21950 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
21951 rs.midDispX = dispX;
21952 rs.midDispY = dispY; // mid source
21953 //
21954
21955 dispX *= -1;
21956 dispY *= -1;
21957
21958 if (isSegments) {
21959 var pts = rs.allpts;
21960
21961 if (pts.length / 2 % 2 === 0) ; else {
21962 var i2 = pts.length / 2 - 1;
21963 var i3 = i2 + 2;
21964 dispX = -(pts[i3] - pts[i2]);
21965 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
21966 }
21967 }
21968
21969 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
21970 //
21971
21972 if (isSegments) {
21973 dispX = endX - rs.segpts[rs.segpts.length - 2];
21974 dispY = endY - rs.segpts[rs.segpts.length - 1];
21975 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21976 var pts = rs.allpts;
21977 var l = pts.length;
21978 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
21979 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
21980 dispX = endX - bX;
21981 dispY = endY - bY;
21982 } else {
21983 dispX = endX - midX;
21984 dispY = endY - midY;
21985 }
21986
21987 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
21988};
21989
21990BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
21991 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
21992 var cachedVal = cache[edgeWidth + ', ' + scale];
21993
21994 if (cachedVal) {
21995 return cachedVal;
21996 }
21997
21998 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
21999 cache[edgeWidth + ', ' + scale] = cachedVal;
22000 return cachedVal;
22001};
22002
22003var BRp$3 = {};
22004
22005BRp$3.findHaystackPoints = function (edges) {
22006 for (var i = 0; i < edges.length; i++) {
22007 var edge = edges[i];
22008 var _p = edge._private;
22009 var rs = _p.rscratch;
22010
22011 if (!rs.haystack) {
22012 var angle = Math.random() * 2 * Math.PI;
22013 rs.source = {
22014 x: Math.cos(angle),
22015 y: Math.sin(angle)
22016 };
22017 angle = Math.random() * 2 * Math.PI;
22018 rs.target = {
22019 x: Math.cos(angle),
22020 y: Math.sin(angle)
22021 };
22022 }
22023
22024 var src = _p.source;
22025 var tgt = _p.target;
22026 var srcPos = src.position();
22027 var tgtPos = tgt.position();
22028 var srcW = src.width();
22029 var tgtW = tgt.width();
22030 var srcH = src.height();
22031 var tgtH = tgt.height();
22032 var radius = edge.pstyle('haystack-radius').value;
22033 var halfRadius = radius / 2; // b/c have to half width/height
22034
22035 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];
22036 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22037 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22038
22039 rs.edgeType = 'haystack';
22040 rs.haystack = true;
22041 this.storeEdgeProjections(edge);
22042 this.calculateArrowAngles(edge);
22043 this.recalculateEdgeLabelProjections(edge);
22044 this.calculateLabelAngles(edge);
22045 }
22046};
22047
22048BRp$3.findSegmentsPoints = function (edge, pairInfo) {
22049 // Segments (multiple straight lines)
22050 var rs = edge._private.rscratch;
22051 var posPts = pairInfo.posPts,
22052 intersectionPts = pairInfo.intersectionPts,
22053 vectorNormInverse = pairInfo.vectorNormInverse;
22054 var edgeDistances = edge.pstyle('edge-distances').value;
22055 var segmentWs = edge.pstyle('segment-weights');
22056 var segmentDs = edge.pstyle('segment-distances');
22057 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22058 rs.edgeType = 'segments';
22059 rs.segpts = [];
22060
22061 for (var s = 0; s < segmentsN; s++) {
22062 var w = segmentWs.pfValue[s];
22063 var d = segmentDs.pfValue[s];
22064 var w1 = 1 - w;
22065 var w2 = w;
22066 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22067 var adjustedMidpt = {
22068 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22069 y: midptPts.y1 * w1 + midptPts.y2 * w2
22070 };
22071 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22072 }
22073};
22074
22075BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22076 // Self-edge
22077 var rs = edge._private.rscratch;
22078 var dirCounts = pairInfo.dirCounts,
22079 srcPos = pairInfo.srcPos;
22080 var ctrlptDists = edge.pstyle('control-point-distances');
22081 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22082 var loopDir = edge.pstyle('loop-direction').pfValue;
22083 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22084 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22085 rs.edgeType = 'self';
22086 var j = i;
22087 var loopDist = stepSize;
22088
22089 if (edgeIsUnbundled) {
22090 j = 0;
22091 loopDist = ctrlptDist;
22092 }
22093
22094 var loopAngle = loopDir - Math.PI / 2;
22095 var outAngle = loopAngle - loopSwp / 2;
22096 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22097
22098 var dc = String(loopDir + '_' + loopSwp);
22099 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22100 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)];
22101};
22102
22103BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22104 // Compound edge
22105 var rs = edge._private.rscratch;
22106 rs.edgeType = 'compound';
22107 var srcPos = pairInfo.srcPos,
22108 tgtPos = pairInfo.tgtPos,
22109 srcW = pairInfo.srcW,
22110 srcH = pairInfo.srcH,
22111 tgtW = pairInfo.tgtW,
22112 tgtH = pairInfo.tgtH;
22113 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22114 var ctrlptDists = edge.pstyle('control-point-distances');
22115 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22116 var j = i;
22117 var loopDist = stepSize;
22118
22119 if (edgeIsUnbundled) {
22120 j = 0;
22121 loopDist = ctrlptDist;
22122 }
22123
22124 var loopW = 50;
22125 var loopaPos = {
22126 x: srcPos.x - srcW / 2,
22127 y: srcPos.y - srcH / 2
22128 };
22129 var loopbPos = {
22130 x: tgtPos.x - tgtW / 2,
22131 y: tgtPos.y - tgtH / 2
22132 };
22133 var loopPos = {
22134 x: Math.min(loopaPos.x, loopbPos.x),
22135 y: Math.min(loopaPos.y, loopbPos.y)
22136 }; // avoids cases with impossible beziers
22137
22138 var minCompoundStretch = 0.5;
22139 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22140 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22141 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];
22142};
22143
22144BRp$3.findStraightEdgePoints = function (edge) {
22145 // Straight edge within bundle
22146 edge._private.rscratch.edgeType = 'straight';
22147};
22148
22149BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22150 var rs = edge._private.rscratch;
22151 var vectorNormInverse = pairInfo.vectorNormInverse,
22152 posPts = pairInfo.posPts,
22153 intersectionPts = pairInfo.intersectionPts;
22154 var edgeDistances = edge.pstyle('edge-distances').value;
22155 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22156 var ctrlptDists = edge.pstyle('control-point-distances');
22157 var ctrlptWs = edge.pstyle('control-point-weights');
22158 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22159 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22160 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22161
22162 var multi = edgeIsUnbundled;
22163 rs.edgeType = multi ? 'multibezier' : 'bezier';
22164 rs.ctrlpts = [];
22165
22166 for (var b = 0; b < bezierN; b++) {
22167 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22168 var manctrlptDist = void 0;
22169 var sign = signum(normctrlptDist);
22170
22171 if (multi) {
22172 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22173
22174 ctrlptWeight = ctrlptWs.value[b];
22175 }
22176
22177 if (edgeIsUnbundled) {
22178 // multi or single unbundled
22179 manctrlptDist = ctrlptDist;
22180 } else {
22181 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22182 }
22183
22184 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22185 var w1 = 1 - ctrlptWeight;
22186 var w2 = ctrlptWeight;
22187 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22188 var adjustedMidpt = {
22189 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22190 y: midptPts.y1 * w1 + midptPts.y2 * w2
22191 };
22192 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22193 }
22194};
22195
22196BRp$3.findTaxiPoints = function (edge, pairInfo) {
22197 // Taxicab geometry with two turns maximum
22198 var rs = edge._private.rscratch;
22199 rs.edgeType = 'segments';
22200 var VERTICAL = 'vertical';
22201 var HORIZONTAL = 'horizontal';
22202 var LEFTWARD = 'leftward';
22203 var RIGHTWARD = 'rightward';
22204 var DOWNWARD = 'downward';
22205 var UPWARD = 'upward';
22206 var AUTO = 'auto';
22207 var posPts = pairInfo.posPts,
22208 srcW = pairInfo.srcW,
22209 srcH = pairInfo.srcH,
22210 tgtW = pairInfo.tgtW,
22211 tgtH = pairInfo.tgtH;
22212 var edgeDistances = edge.pstyle('edge-distances').value;
22213 var dIncludesNodeBody = edgeDistances !== 'node-position';
22214 var taxiDir = edge.pstyle('taxi-direction').value;
22215 var rawTaxiDir = taxiDir; // unprocessed value
22216
22217 var taxiTurn = edge.pstyle('taxi-turn');
22218 var turnIsPercent = taxiTurn.units === '%';
22219 var taxiTurnPfVal = turnIsPercent && taxiTurn.pfValue < 0 ? 1 + taxiTurn.pfValue : taxiTurn.pfValue;
22220 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22221 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22222 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22223 var pdx = posPts.x2 - posPts.x1;
22224 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
22225
22226 var subDWH = function subDWH(dxy, dwh) {
22227 if (dxy > 0) {
22228 return Math.max(dxy - dwh, 0);
22229 } else {
22230 return Math.min(dxy + dwh, 0);
22231 }
22232 };
22233
22234 var dx = subDWH(pdx, dw);
22235 var dy = subDWH(pdy, dh);
22236 var isExplicitDir = false;
22237
22238 if (taxiDir === AUTO) {
22239 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22240 } else if (taxiDir === UPWARD || taxiDir === DOWNWARD) {
22241 taxiDir = VERTICAL;
22242 isExplicitDir = true;
22243 } else if (taxiDir === LEFTWARD || taxiDir === RIGHTWARD) {
22244 taxiDir = HORIZONTAL;
22245 isExplicitDir = true;
22246 }
22247
22248 var isVert = taxiDir === VERTICAL;
22249 var l = isVert ? dy : dx;
22250 var pl = isVert ? pdy : pdx;
22251 var sgnL = signum(pl);
22252 var forcedDir = false;
22253
22254 if (!(isExplicitDir && turnIsPercent) // forcing in this case would cause weird growing in the opposite direction
22255 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22256 sgnL *= -1;
22257 l = sgnL * Math.abs(l);
22258 forcedDir = true;
22259 }
22260
22261 var d = turnIsPercent ? taxiTurnPfVal * l : taxiTurnPfVal * sgnL;
22262
22263 var getIsTooClose = function getIsTooClose(d) {
22264 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22265 };
22266
22267 var isTooCloseSrc = getIsTooClose(d);
22268 var isTooCloseTgt = getIsTooClose(l - Math.abs(d));
22269 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22270
22271 if (isTooClose && !forcedDir) {
22272 // non-ideal routing
22273 if (isVert) {
22274 // vertical fallbacks
22275 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22276 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22277
22278 if (lShapeInsideSrc) {
22279 // horizontal Z-shape (direction not respected)
22280 var x = (posPts.x1 + posPts.x2) / 2;
22281 var y1 = posPts.y1,
22282 y2 = posPts.y2;
22283 rs.segpts = [x, y1, x, y2];
22284 } else if (lShapeInsideTgt) {
22285 // vertical Z-shape (distance not respected)
22286 var y = (posPts.y1 + posPts.y2) / 2;
22287 var x1 = posPts.x1,
22288 x2 = posPts.x2;
22289 rs.segpts = [x1, y, x2, y];
22290 } else {
22291 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22292 rs.segpts = [posPts.x1, posPts.y2];
22293 }
22294 } else {
22295 // horizontal fallbacks
22296 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22297
22298 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22299
22300 if (_lShapeInsideSrc) {
22301 // vertical Z-shape (direction not respected)
22302 var _y = (posPts.y1 + posPts.y2) / 2;
22303
22304 var _x = posPts.x1,
22305 _x2 = posPts.x2;
22306 rs.segpts = [_x, _y, _x2, _y];
22307 } else if (_lShapeInsideTgt) {
22308 // horizontal Z-shape (turn distance not respected)
22309 var _x3 = (posPts.x1 + posPts.x2) / 2;
22310
22311 var _y2 = posPts.y1,
22312 _y3 = posPts.y2;
22313 rs.segpts = [_x3, _y2, _x3, _y3];
22314 } else {
22315 // L-shape (turn distance not respected, but works well for tree siblings)
22316 rs.segpts = [posPts.x2, posPts.y1];
22317 }
22318 }
22319 } else {
22320 // ideal routing
22321 if (isVert) {
22322 var _y4 = (d < 0 ? posPts.y2 : posPts.y1) + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22323
22324 var _x4 = posPts.x1,
22325 _x5 = posPts.x2;
22326 rs.segpts = [_x4, _y4, _x5, _y4];
22327 } else {
22328 // horizontal
22329 var _x6 = (d < 0 ? posPts.x2 : posPts.x1) + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22330
22331 var _y5 = posPts.y1,
22332 _y6 = posPts.y2;
22333 rs.segpts = [_x6, _y5, _x6, _y6];
22334 }
22335 }
22336};
22337
22338BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22339 var rs = edge._private.rscratch; // can only correct beziers for now...
22340
22341 if (rs.edgeType === 'bezier') {
22342 var srcPos = pairInfo.srcPos,
22343 tgtPos = pairInfo.tgtPos,
22344 srcW = pairInfo.srcW,
22345 srcH = pairInfo.srcH,
22346 tgtW = pairInfo.tgtW,
22347 tgtH = pairInfo.tgtH,
22348 srcShape = pairInfo.srcShape,
22349 tgtShape = pairInfo.tgtShape;
22350 var badStart = !number(rs.startX) || !number(rs.startY);
22351 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
22352 var badEnd = !number(rs.endX) || !number(rs.endY);
22353 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
22354 var minCpADistFactor = 3;
22355 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22356 var minCpADist = minCpADistFactor * arrowW;
22357 var startACpDist = dist({
22358 x: rs.ctrlpts[0],
22359 y: rs.ctrlpts[1]
22360 }, {
22361 x: rs.startX,
22362 y: rs.startY
22363 });
22364 var closeStartACp = startACpDist < minCpADist;
22365 var endACpDist = dist({
22366 x: rs.ctrlpts[0],
22367 y: rs.ctrlpts[1]
22368 }, {
22369 x: rs.endX,
22370 y: rs.endY
22371 });
22372 var closeEndACp = endACpDist < minCpADist;
22373 var overlapping = false;
22374
22375 if (badStart || badAStart || closeStartACp) {
22376 overlapping = true; // project control point along line from src centre to outside the src shape
22377 // (otherwise intersection will yield nothing)
22378
22379 var cpD = {
22380 // delta
22381 x: rs.ctrlpts[0] - srcPos.x,
22382 y: rs.ctrlpts[1] - srcPos.y
22383 };
22384 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22385
22386 var cpM = {
22387 // normalised delta
22388 x: cpD.x / cpL,
22389 y: cpD.y / cpL
22390 };
22391 var radius = Math.max(srcW, srcH);
22392 var cpProj = {
22393 // *2 radius guarantees outside shape
22394 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22395 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22396 };
22397 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
22398
22399 if (closeStartACp) {
22400 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22401 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22402 } else {
22403 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22404 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22405 }
22406 }
22407
22408 if (badEnd || badAEnd || closeEndACp) {
22409 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
22410 // (otherwise intersection will yield nothing)
22411
22412 var _cpD = {
22413 // delta
22414 x: rs.ctrlpts[0] - tgtPos.x,
22415 y: rs.ctrlpts[1] - tgtPos.y
22416 };
22417
22418 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22419
22420
22421 var _cpM = {
22422 // normalised delta
22423 x: _cpD.x / _cpL,
22424 y: _cpD.y / _cpL
22425 };
22426
22427 var _radius = Math.max(srcW, srcH);
22428
22429 var _cpProj = {
22430 // *2 radius guarantees outside shape
22431 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22432 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22433 };
22434 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
22435
22436 if (closeEndACp) {
22437 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22438 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22439 } else {
22440 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22441 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22442 }
22443 }
22444
22445 if (overlapping) {
22446 // recalc endpts
22447 this.findEndpoints(edge);
22448 }
22449 }
22450};
22451
22452BRp$3.storeAllpts = function (edge) {
22453 var rs = edge._private.rscratch;
22454
22455 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22456 rs.allpts = [];
22457 rs.allpts.push(rs.startX, rs.startY);
22458
22459 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22460 // ctrl pt itself
22461 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
22462
22463 if (b + 3 < rs.ctrlpts.length) {
22464 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22465 }
22466 }
22467
22468 rs.allpts.push(rs.endX, rs.endY);
22469 var m, mt;
22470
22471 if (rs.ctrlpts.length / 2 % 2 === 0) {
22472 m = rs.allpts.length / 2 - 1;
22473 rs.midX = rs.allpts[m];
22474 rs.midY = rs.allpts[m + 1];
22475 } else {
22476 m = rs.allpts.length / 2 - 3;
22477 mt = 0.5;
22478 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22479 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22480 }
22481 } else if (rs.edgeType === 'straight') {
22482 // need to calc these after endpts
22483 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
22484
22485 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22486 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22487 } else if (rs.edgeType === 'segments') {
22488 rs.allpts = [];
22489 rs.allpts.push(rs.startX, rs.startY);
22490 rs.allpts.push.apply(rs.allpts, rs.segpts);
22491 rs.allpts.push(rs.endX, rs.endY);
22492
22493 if (rs.segpts.length % 4 === 0) {
22494 var i2 = rs.segpts.length / 2;
22495 var i1 = i2 - 2;
22496 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22497 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22498 } else {
22499 var _i = rs.segpts.length / 2 - 1;
22500
22501 rs.midX = rs.segpts[_i];
22502 rs.midY = rs.segpts[_i + 1];
22503 }
22504 }
22505};
22506
22507BRp$3.checkForInvalidEdgeWarning = function (edge) {
22508 var rs = edge[0]._private.rscratch;
22509
22510 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
22511 rs.loggedErr = false;
22512 } else {
22513 if (!rs.loggedErr) {
22514 rs.loggedErr = true;
22515 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.');
22516 }
22517 }
22518};
22519
22520BRp$3.findEdgeControlPoints = function (edges) {
22521 var _this = this;
22522
22523 if (!edges || edges.length === 0) {
22524 return;
22525 }
22526
22527 var r = this;
22528 var cy = r.cy;
22529 var hasCompounds = cy.hasCompoundNodes();
22530 var hashTable = {
22531 map: new Map$1(),
22532 get: function get(pairId) {
22533 var map2 = this.map.get(pairId[0]);
22534
22535 if (map2 != null) {
22536 return map2.get(pairId[1]);
22537 } else {
22538 return null;
22539 }
22540 },
22541 set: function set(pairId, val) {
22542 var map2 = this.map.get(pairId[0]);
22543
22544 if (map2 == null) {
22545 map2 = new Map$1();
22546 this.map.set(pairId[0], map2);
22547 }
22548
22549 map2.set(pairId[1], val);
22550 }
22551 };
22552 var pairIds = [];
22553 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
22554
22555 for (var i = 0; i < edges.length; i++) {
22556 var edge = edges[i];
22557 var _p = edge._private;
22558 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
22559 // they shouldn't take up space
22560
22561 if (edge.removed() || !edge.takesUpSpace()) {
22562 continue;
22563 }
22564
22565 if (curveStyle === 'haystack') {
22566 haystackEdges.push(edge);
22567 continue;
22568 }
22569
22570 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
22571 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
22572 var src = _p.source;
22573 var tgt = _p.target;
22574 var srcIndex = src.poolIndex();
22575 var tgtIndex = tgt.poolIndex();
22576 var pairId = [srcIndex, tgtIndex].sort();
22577 var tableEntry = hashTable.get(pairId);
22578
22579 if (tableEntry == null) {
22580 tableEntry = {
22581 eles: []
22582 };
22583 hashTable.set(pairId, tableEntry);
22584 pairIds.push(pairId);
22585 }
22586
22587 tableEntry.eles.push(edge);
22588
22589 if (edgeIsUnbundled) {
22590 tableEntry.hasUnbundled = true;
22591 }
22592
22593 if (edgeIsBezier) {
22594 tableEntry.hasBezier = true;
22595 }
22596 } // for each pair (src, tgt), create the ctrl pts
22597 // Nested for loop is OK; total number of iterations for both loops = edgeCount
22598
22599
22600 var _loop = function _loop(p) {
22601 var pairId = pairIds[p];
22602 var pairInfo = hashTable.get(pairId);
22603 var swappedpairInfo = void 0;
22604
22605 if (!pairInfo.hasUnbundled) {
22606 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
22607 return e.isBundledBezier();
22608 });
22609 clearArray(pairInfo.eles);
22610 pllEdges.forEach(function (edge) {
22611 return pairInfo.eles.push(edge);
22612 }); // for each pair id, the edges should be sorted by index
22613
22614 pairInfo.eles.sort(function (edge1, edge2) {
22615 return edge1.poolIndex() - edge2.poolIndex();
22616 });
22617 }
22618
22619 var firstEdge = pairInfo.eles[0];
22620 var src = firstEdge.source();
22621 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
22622
22623 if (src.poolIndex() > tgt.poolIndex()) {
22624 var temp = src;
22625 src = tgt;
22626 tgt = temp;
22627 }
22628
22629 var srcPos = pairInfo.srcPos = src.position();
22630 var tgtPos = pairInfo.tgtPos = tgt.position();
22631 var srcW = pairInfo.srcW = src.outerWidth();
22632 var srcH = pairInfo.srcH = src.outerHeight();
22633 var tgtW = pairInfo.tgtW = tgt.outerWidth();
22634 var tgtH = pairInfo.tgtH = tgt.outerHeight();
22635
22636 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
22637
22638 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
22639
22640 pairInfo.dirCounts = {
22641 'north': 0,
22642 'west': 0,
22643 'south': 0,
22644 'east': 0,
22645 'northwest': 0,
22646 'southwest': 0,
22647 'northeast': 0,
22648 'southeast': 0
22649 };
22650
22651 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
22652 var _edge = pairInfo.eles[_i2];
22653 var rs = _edge[0]._private.rscratch;
22654
22655 var _curveStyle = _edge.pstyle('curve-style').value;
22656
22657 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
22658
22659
22660 var edgeIsSwapped = !src.same(_edge.source());
22661
22662 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
22663 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
22664
22665 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
22666 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
22667
22668 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
22669 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
22670 var intersectionPts = pairInfo.intersectionPts = {
22671 x1: srcOutside[0],
22672 x2: tgtOutside[0],
22673 y1: srcOutside[1],
22674 y2: tgtOutside[1]
22675 };
22676 var posPts = pairInfo.posPts = {
22677 x1: srcPos.x,
22678 x2: tgtPos.x,
22679 y1: srcPos.y,
22680 y2: tgtPos.y
22681 };
22682 var dy = tgtOutside[1] - srcOutside[1];
22683 var dx = tgtOutside[0] - srcOutside[0];
22684 var l = Math.sqrt(dx * dx + dy * dy);
22685 var vector = pairInfo.vector = {
22686 x: dx,
22687 y: dy
22688 };
22689 var vectorNorm = pairInfo.vectorNorm = {
22690 x: vector.x / l,
22691 y: vector.y / l
22692 };
22693 var vectorNormInverse = {
22694 x: -vectorNorm.y,
22695 y: vectorNorm.x
22696 }; // if node shapes overlap, then no ctrl pts to draw
22697
22698 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);
22699 pairInfo.vectorNormInverse = vectorNormInverse;
22700 swappedpairInfo = {
22701 nodesOverlap: pairInfo.nodesOverlap,
22702 dirCounts: pairInfo.dirCounts,
22703 calculatedIntersection: true,
22704 hasBezier: pairInfo.hasBezier,
22705 hasUnbundled: pairInfo.hasUnbundled,
22706 eles: pairInfo.eles,
22707 srcPos: tgtPos,
22708 tgtPos: srcPos,
22709 srcW: tgtW,
22710 srcH: tgtH,
22711 tgtW: srcW,
22712 tgtH: srcH,
22713 srcIntn: tgtIntn,
22714 tgtIntn: srcIntn,
22715 srcShape: tgtShape,
22716 tgtShape: srcShape,
22717 posPts: {
22718 x1: posPts.x2,
22719 y1: posPts.y2,
22720 x2: posPts.x1,
22721 y2: posPts.y1
22722 },
22723 intersectionPts: {
22724 x1: intersectionPts.x2,
22725 y1: intersectionPts.y2,
22726 x2: intersectionPts.x1,
22727 y2: intersectionPts.y1
22728 },
22729 vector: {
22730 x: -vector.x,
22731 y: -vector.y
22732 },
22733 vectorNorm: {
22734 x: -vectorNorm.x,
22735 y: -vectorNorm.y
22736 },
22737 vectorNormInverse: {
22738 x: -vectorNormInverse.x,
22739 y: -vectorNormInverse.y
22740 }
22741 };
22742 }
22743
22744 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
22745 rs.nodesOverlap = passedPairInfo.nodesOverlap;
22746 rs.srcIntn = passedPairInfo.srcIntn;
22747 rs.tgtIntn = passedPairInfo.tgtIntn;
22748
22749 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
22750 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22751 } else if (src === tgt) {
22752 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22753 } else if (_curveStyle === 'segments') {
22754 _this.findSegmentsPoints(_edge, passedPairInfo);
22755 } else if (_curveStyle === 'taxi') {
22756 _this.findTaxiPoints(_edge, passedPairInfo);
22757 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
22758 _this.findStraightEdgePoints(_edge);
22759 } else {
22760 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
22761 }
22762
22763 _this.findEndpoints(_edge);
22764
22765 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
22766
22767 _this.checkForInvalidEdgeWarning(_edge);
22768
22769 _this.storeAllpts(_edge);
22770
22771 _this.storeEdgeProjections(_edge);
22772
22773 _this.calculateArrowAngles(_edge);
22774
22775 _this.recalculateEdgeLabelProjections(_edge);
22776
22777 _this.calculateLabelAngles(_edge);
22778 } // for pair edges
22779
22780 };
22781
22782 for (var p = 0; p < pairIds.length; p++) {
22783 _loop(p);
22784 } // for pair ids
22785 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
22786
22787
22788 this.findHaystackPoints(haystackEdges);
22789};
22790
22791function getPts(pts) {
22792 var retPts = [];
22793
22794 if (pts == null) {
22795 return;
22796 }
22797
22798 for (var i = 0; i < pts.length; i += 2) {
22799 var x = pts[i];
22800 var y = pts[i + 1];
22801 retPts.push({
22802 x: x,
22803 y: y
22804 });
22805 }
22806
22807 return retPts;
22808}
22809
22810BRp$3.getSegmentPoints = function (edge) {
22811 var rs = edge[0]._private.rscratch;
22812 var type = rs.edgeType;
22813
22814 if (type === 'segments') {
22815 this.recalculateRenderedStyle(edge);
22816 return getPts(rs.segpts);
22817 }
22818};
22819
22820BRp$3.getControlPoints = function (edge) {
22821 var rs = edge[0]._private.rscratch;
22822 var type = rs.edgeType;
22823
22824 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
22825 this.recalculateRenderedStyle(edge);
22826 return getPts(rs.ctrlpts);
22827 }
22828};
22829
22830BRp$3.getEdgeMidpoint = function (edge) {
22831 var rs = edge[0]._private.rscratch;
22832 this.recalculateRenderedStyle(edge);
22833 return {
22834 x: rs.midX,
22835 y: rs.midY
22836 };
22837};
22838
22839var BRp$4 = {};
22840
22841BRp$4.manualEndptToPx = function (node, prop) {
22842 var r = this;
22843 var npos = node.position();
22844 var w = node.outerWidth();
22845 var h = node.outerHeight();
22846
22847 if (prop.value.length === 2) {
22848 var p = [prop.pfValue[0], prop.pfValue[1]];
22849
22850 if (prop.units[0] === '%') {
22851 p[0] = p[0] * w;
22852 }
22853
22854 if (prop.units[1] === '%') {
22855 p[1] = p[1] * h;
22856 }
22857
22858 p[0] += npos.x;
22859 p[1] += npos.y;
22860 return p;
22861 } else {
22862 var angle = prop.pfValue[0];
22863 angle = -Math.PI / 2 + angle; // start at 12 o'clock
22864
22865 var l = 2 * Math.max(w, h);
22866 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
22867 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
22868 }
22869};
22870
22871BRp$4.findEndpoints = function (edge) {
22872 var r = this;
22873 var intersect;
22874 var source = edge.source()[0];
22875 var target = edge.target()[0];
22876 var srcPos = source.position();
22877 var tgtPos = target.position();
22878 var tgtArShape = edge.pstyle('target-arrow-shape').value;
22879 var srcArShape = edge.pstyle('source-arrow-shape').value;
22880 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
22881 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
22882 var curveStyle = edge.pstyle('curve-style').value;
22883 var rs = edge._private.rscratch;
22884 var et = rs.edgeType;
22885 var taxi = curveStyle === 'taxi';
22886 var self = et === 'self' || et === 'compound';
22887 var bezier = et === 'bezier' || et === 'multibezier' || self;
22888 var multi = et !== 'bezier';
22889 var lines = et === 'straight' || et === 'segments';
22890 var segments = et === 'segments';
22891 var hasEndpts = bezier || multi || lines;
22892 var overrideEndpts = self || taxi;
22893 var srcManEndpt = edge.pstyle('source-endpoint');
22894 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
22895 var tgtManEndpt = edge.pstyle('target-endpoint');
22896 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
22897 rs.srcManEndpt = srcManEndpt;
22898 rs.tgtManEndpt = tgtManEndpt;
22899 var p1; // last known point of edge on target side
22900
22901 var p2; // last known point of edge on source side
22902
22903 var p1_i; // point to intersect with target shape
22904
22905 var p2_i; // point to intersect with source shape
22906
22907 if (bezier) {
22908 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
22909 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
22910 p1 = cpEnd;
22911 p2 = cpStart;
22912 } else if (lines) {
22913 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
22914 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
22915 p1 = tgtArrowFromPt;
22916 p2 = srcArrowFromPt;
22917 }
22918
22919 if (tgtManEndptVal === 'inside-to-node') {
22920 intersect = [tgtPos.x, tgtPos.y];
22921 } else if (tgtManEndpt.units) {
22922 intersect = this.manualEndptToPx(target, tgtManEndpt);
22923 } else if (tgtManEndptVal === 'outside-to-line') {
22924 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
22925 } else {
22926 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
22927 p1_i = p1;
22928 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
22929 p1_i = [srcPos.x, srcPos.y];
22930 }
22931
22932 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
22933
22934 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
22935 var trs = target._private.rscratch;
22936 var lw = trs.labelWidth;
22937 var lh = trs.labelHeight;
22938 var lx = trs.labelX;
22939 var ly = trs.labelY;
22940 var lw2 = lw / 2;
22941 var lh2 = lh / 2;
22942 var va = target.pstyle('text-valign').value;
22943
22944 if (va === 'top') {
22945 ly -= lh2;
22946 } else if (va === 'bottom') {
22947 ly += lh2;
22948 }
22949
22950 var ha = target.pstyle('text-halign').value;
22951
22952 if (ha === 'left') {
22953 lx -= lw2;
22954 } else if (ha === 'right') {
22955 lx += lw2;
22956 }
22957
22958 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);
22959
22960 if (labelIntersect.length > 0) {
22961 var refPt = srcPos;
22962 var intSqdist = sqdist(refPt, array2point(intersect));
22963 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
22964 var minSqDist = intSqdist;
22965
22966 if (labIntSqdist < intSqdist) {
22967 intersect = labelIntersect;
22968 minSqDist = labIntSqdist;
22969 }
22970
22971 if (labelIntersect.length > 2) {
22972 var labInt2SqDist = sqdist(refPt, {
22973 x: labelIntersect[2],
22974 y: labelIntersect[3]
22975 });
22976
22977 if (labInt2SqDist < minSqDist) {
22978 intersect = [labelIntersect[2], labelIntersect[3]];
22979 }
22980 }
22981 }
22982 }
22983 }
22984
22985 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
22986 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
22987 rs.endX = edgeEnd[0];
22988 rs.endY = edgeEnd[1];
22989 rs.arrowEndX = arrowEnd[0];
22990 rs.arrowEndY = arrowEnd[1];
22991
22992 if (srcManEndptVal === 'inside-to-node') {
22993 intersect = [srcPos.x, srcPos.y];
22994 } else if (srcManEndpt.units) {
22995 intersect = this.manualEndptToPx(source, srcManEndpt);
22996 } else if (srcManEndptVal === 'outside-to-line') {
22997 intersect = rs.srcIntn; // use cached value from ctrlpt calc
22998 } else {
22999 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23000 p2_i = p2;
23001 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23002 p2_i = [tgtPos.x, tgtPos.y];
23003 }
23004
23005 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23006
23007 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23008 var srs = source._private.rscratch;
23009 var _lw = srs.labelWidth;
23010 var _lh = srs.labelHeight;
23011 var _lx = srs.labelX;
23012 var _ly = srs.labelY;
23013
23014 var _lw2 = _lw / 2;
23015
23016 var _lh2 = _lh / 2;
23017
23018 var _va = source.pstyle('text-valign').value;
23019
23020 if (_va === 'top') {
23021 _ly -= _lh2;
23022 } else if (_va === 'bottom') {
23023 _ly += _lh2;
23024 }
23025
23026 var _ha = source.pstyle('text-halign').value;
23027
23028 if (_ha === 'left') {
23029 _lx -= _lw2;
23030 } else if (_ha === 'right') {
23031 _lx += _lw2;
23032 }
23033
23034 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);
23035
23036 if (_labelIntersect.length > 0) {
23037 var _refPt = tgtPos;
23038
23039 var _intSqdist = sqdist(_refPt, array2point(intersect));
23040
23041 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23042
23043 var _minSqDist = _intSqdist;
23044
23045 if (_labIntSqdist < _intSqdist) {
23046 intersect = [_labelIntersect[0], _labelIntersect[1]];
23047 _minSqDist = _labIntSqdist;
23048 }
23049
23050 if (_labelIntersect.length > 2) {
23051 var _labInt2SqDist = sqdist(_refPt, {
23052 x: _labelIntersect[2],
23053 y: _labelIntersect[3]
23054 });
23055
23056 if (_labInt2SqDist < _minSqDist) {
23057 intersect = [_labelIntersect[2], _labelIntersect[3]];
23058 }
23059 }
23060 }
23061 }
23062 }
23063
23064 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23065 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23066 rs.startX = edgeStart[0];
23067 rs.startY = edgeStart[1];
23068 rs.arrowStartX = arrowStart[0];
23069 rs.arrowStartY = arrowStart[1];
23070
23071 if (hasEndpts) {
23072 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
23073 rs.badLine = true;
23074 } else {
23075 rs.badLine = false;
23076 }
23077 }
23078};
23079
23080BRp$4.getSourceEndpoint = function (edge) {
23081 var rs = edge[0]._private.rscratch;
23082 this.recalculateRenderedStyle(edge);
23083
23084 switch (rs.edgeType) {
23085 case 'haystack':
23086 return {
23087 x: rs.haystackPts[0],
23088 y: rs.haystackPts[1]
23089 };
23090
23091 default:
23092 return {
23093 x: rs.arrowStartX,
23094 y: rs.arrowStartY
23095 };
23096 }
23097};
23098
23099BRp$4.getTargetEndpoint = function (edge) {
23100 var rs = edge[0]._private.rscratch;
23101 this.recalculateRenderedStyle(edge);
23102
23103 switch (rs.edgeType) {
23104 case 'haystack':
23105 return {
23106 x: rs.haystackPts[2],
23107 y: rs.haystackPts[3]
23108 };
23109
23110 default:
23111 return {
23112 x: rs.arrowEndX,
23113 y: rs.arrowEndY
23114 };
23115 }
23116};
23117
23118var BRp$5 = {};
23119
23120function pushBezierPts(r, edge, pts) {
23121 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23122 return qbezierAt(p1, p2, p3, t);
23123 };
23124
23125 var _p = edge._private;
23126 var bpts = _p.rstyle.bezierPts;
23127
23128 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23129 var p = r.bezierProjPcts[i];
23130 bpts.push({
23131 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23132 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23133 });
23134 }
23135}
23136
23137BRp$5.storeEdgeProjections = function (edge) {
23138 var _p = edge._private;
23139 var rs = _p.rscratch;
23140 var et = rs.edgeType; // clear the cached points state
23141
23142 _p.rstyle.bezierPts = null;
23143 _p.rstyle.linePts = null;
23144 _p.rstyle.haystackPts = null;
23145
23146 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23147 _p.rstyle.bezierPts = [];
23148
23149 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23150 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23151 }
23152 } else if (et === 'segments') {
23153 var lpts = _p.rstyle.linePts = [];
23154
23155 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23156 lpts.push({
23157 x: rs.allpts[i],
23158 y: rs.allpts[i + 1]
23159 });
23160 }
23161 } else if (et === 'haystack') {
23162 var hpts = rs.haystackPts;
23163 _p.rstyle.haystackPts = [{
23164 x: hpts[0],
23165 y: hpts[1]
23166 }, {
23167 x: hpts[2],
23168 y: hpts[3]
23169 }];
23170 }
23171
23172 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23173};
23174
23175BRp$5.recalculateEdgeProjections = function (edges) {
23176 this.findEdgeControlPoints(edges);
23177};
23178
23179var BRp$6 = {};
23180
23181BRp$6.recalculateNodeLabelProjection = function (node) {
23182 var content = node.pstyle('label').strValue;
23183
23184 if (emptyString(content)) {
23185 return;
23186 }
23187
23188 var textX, textY;
23189 var _p = node._private;
23190 var nodeWidth = node.width();
23191 var nodeHeight = node.height();
23192 var padding = node.padding();
23193 var nodePos = node.position();
23194 var textHalign = node.pstyle('text-halign').strValue;
23195 var textValign = node.pstyle('text-valign').strValue;
23196 var rs = _p.rscratch;
23197 var rstyle = _p.rstyle;
23198
23199 switch (textHalign) {
23200 case 'left':
23201 textX = nodePos.x - nodeWidth / 2 - padding;
23202 break;
23203
23204 case 'right':
23205 textX = nodePos.x + nodeWidth / 2 + padding;
23206 break;
23207
23208 default:
23209 // e.g. center
23210 textX = nodePos.x;
23211 }
23212
23213 switch (textValign) {
23214 case 'top':
23215 textY = nodePos.y - nodeHeight / 2 - padding;
23216 break;
23217
23218 case 'bottom':
23219 textY = nodePos.y + nodeHeight / 2 + padding;
23220 break;
23221
23222 default:
23223 // e.g. middle
23224 textY = nodePos.y;
23225 }
23226
23227 rs.labelX = textX;
23228 rs.labelY = textY;
23229 rstyle.labelX = textX;
23230 rstyle.labelY = textY;
23231 this.applyLabelDimensions(node);
23232};
23233
23234var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23235 var angle = Math.atan(dy / dx);
23236
23237 if (dx === 0 && angle < 0) {
23238 angle = angle * -1;
23239 }
23240
23241 return angle;
23242};
23243
23244var lineAngle = function lineAngle(p0, p1) {
23245 var dx = p1.x - p0.x;
23246 var dy = p1.y - p0.y;
23247 return lineAngleFromDelta(dx, dy);
23248};
23249
23250var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23251 var t0 = bound(0, t - 0.001, 1);
23252 var t1 = bound(0, t + 0.001, 1);
23253 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23254 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23255 return lineAngle(lp0, lp1);
23256};
23257
23258BRp$6.recalculateEdgeLabelProjections = function (edge) {
23259 var p;
23260 var _p = edge._private;
23261 var rs = _p.rscratch;
23262 var r = this;
23263 var content = {
23264 mid: edge.pstyle('label').strValue,
23265 source: edge.pstyle('source-label').strValue,
23266 target: edge.pstyle('target-label').strValue
23267 };
23268
23269 if (content.mid || content.source || content.target) ; else {
23270 return; // no labels => no calcs
23271 } // add center point to style so bounding box calculations can use it
23272 //
23273
23274
23275 p = {
23276 x: rs.midX,
23277 y: rs.midY
23278 };
23279
23280 var setRs = function setRs(propName, prefix, value) {
23281 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23282 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23283 };
23284
23285 setRs('labelX', null, p.x);
23286 setRs('labelY', null, p.y);
23287 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23288 setRs('labelAutoAngle', null, midAngle);
23289
23290 var createControlPointInfo = function createControlPointInfo() {
23291 if (createControlPointInfo.cache) {
23292 return createControlPointInfo.cache;
23293 } // use cache so only 1x per edge
23294
23295
23296 var ctrlpts = []; // store each ctrlpt info init
23297
23298 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23299 var p0 = {
23300 x: rs.allpts[i],
23301 y: rs.allpts[i + 1]
23302 };
23303 var p1 = {
23304 x: rs.allpts[i + 2],
23305 y: rs.allpts[i + 3]
23306 }; // ctrlpt
23307
23308 var p2 = {
23309 x: rs.allpts[i + 4],
23310 y: rs.allpts[i + 5]
23311 };
23312 ctrlpts.push({
23313 p0: p0,
23314 p1: p1,
23315 p2: p2,
23316 startDist: 0,
23317 length: 0,
23318 segments: []
23319 });
23320 }
23321
23322 var bpts = _p.rstyle.bezierPts;
23323 var nProjs = r.bezierProjPcts.length;
23324
23325 function addSegment(cp, p0, p1, t0, t1) {
23326 var length = dist(p0, p1);
23327 var prevSegment = cp.segments[cp.segments.length - 1];
23328 var segment = {
23329 p0: p0,
23330 p1: p1,
23331 t0: t0,
23332 t1: t1,
23333 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23334 length: length
23335 };
23336 cp.segments.push(segment);
23337 cp.length += length;
23338 } // update each ctrlpt with segment info
23339
23340
23341 for (var _i = 0; _i < ctrlpts.length; _i++) {
23342 var cp = ctrlpts[_i];
23343 var prevCp = ctrlpts[_i - 1];
23344
23345 if (prevCp) {
23346 cp.startDist = prevCp.startDist + prevCp.length;
23347 }
23348
23349 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23350
23351 for (var j = 0; j < nProjs - 1; j++) {
23352 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23353 }
23354
23355 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23356 }
23357
23358 return createControlPointInfo.cache = ctrlpts;
23359 };
23360
23361 var calculateEndProjection = function calculateEndProjection(prefix) {
23362 var angle;
23363 var isSrc = prefix === 'source';
23364
23365 if (!content[prefix]) {
23366 return;
23367 }
23368
23369 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23370
23371 switch (rs.edgeType) {
23372 case 'self':
23373 case 'compound':
23374 case 'bezier':
23375 case 'multibezier':
23376 {
23377 var cps = createControlPointInfo();
23378 var selected;
23379 var startDist = 0;
23380 var totalDist = 0; // find the segment we're on
23381
23382 for (var i = 0; i < cps.length; i++) {
23383 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23384
23385 for (var j = 0; j < _cp.segments.length; j++) {
23386 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23387 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23388 startDist = totalDist;
23389 totalDist += _seg.length;
23390
23391 if (totalDist >= offset || lastSeg) {
23392 selected = {
23393 cp: _cp,
23394 segment: _seg
23395 };
23396 break;
23397 }
23398 }
23399
23400 if (selected) {
23401 break;
23402 }
23403 }
23404
23405 var cp = selected.cp;
23406 var seg = selected.segment;
23407 var tSegment = (offset - startDist) / seg.length;
23408 var segDt = seg.t1 - seg.t0;
23409 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23410 t = bound(0, t, 1);
23411 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23412 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23413 break;
23414 }
23415
23416 case 'straight':
23417 case 'segments':
23418 case 'haystack':
23419 {
23420 var d = 0,
23421 di,
23422 d0;
23423 var p0, p1;
23424 var l = rs.allpts.length;
23425
23426 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23427 if (isSrc) {
23428 p0 = {
23429 x: rs.allpts[_i2],
23430 y: rs.allpts[_i2 + 1]
23431 };
23432 p1 = {
23433 x: rs.allpts[_i2 + 2],
23434 y: rs.allpts[_i2 + 3]
23435 };
23436 } else {
23437 p0 = {
23438 x: rs.allpts[l - 2 - _i2],
23439 y: rs.allpts[l - 1 - _i2]
23440 };
23441 p1 = {
23442 x: rs.allpts[l - 4 - _i2],
23443 y: rs.allpts[l - 3 - _i2]
23444 };
23445 }
23446
23447 di = dist(p0, p1);
23448 d0 = d;
23449 d += di;
23450
23451 if (d >= offset) {
23452 break;
23453 }
23454 }
23455
23456 var pD = offset - d0;
23457
23458 var _t = pD / di;
23459
23460 _t = bound(0, _t, 1);
23461 p = lineAt(p0, p1, _t);
23462 angle = lineAngle(p0, p1);
23463 break;
23464 }
23465 }
23466
23467 setRs('labelX', prefix, p.x);
23468 setRs('labelY', prefix, p.y);
23469 setRs('labelAutoAngle', prefix, angle);
23470 };
23471
23472 calculateEndProjection('source');
23473 calculateEndProjection('target');
23474 this.applyLabelDimensions(edge);
23475};
23476
23477BRp$6.applyLabelDimensions = function (ele) {
23478 this.applyPrefixedLabelDimensions(ele);
23479
23480 if (ele.isEdge()) {
23481 this.applyPrefixedLabelDimensions(ele, 'source');
23482 this.applyPrefixedLabelDimensions(ele, 'target');
23483 }
23484};
23485
23486BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
23487 var _p = ele._private;
23488 var text = this.getLabelText(ele, prefix);
23489 var labelDims = this.calculateLabelDimensions(ele, text);
23490 var lineHeight = ele.pstyle('line-height').pfValue;
23491 var textWrap = ele.pstyle('text-wrap').strValue;
23492 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23493 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23494 var normPerLineHeight = labelDims.height / numLines;
23495 var labelLineHeight = normPerLineHeight * lineHeight;
23496 var width = labelDims.width;
23497 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23498 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23499 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23500 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23501 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23502 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23503};
23504
23505BRp$6.getLabelText = function (ele, prefix) {
23506 var _p = ele._private;
23507 var pfd = prefix ? prefix + '-' : '';
23508 var text = ele.pstyle(pfd + 'label').strValue;
23509 var textTransform = ele.pstyle('text-transform').value;
23510
23511 var rscratch = function rscratch(propName, value) {
23512 if (value) {
23513 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23514 return value;
23515 } else {
23516 return getPrefixedProperty(_p.rscratch, propName, prefix);
23517 }
23518 }; // for empty text, skip all processing
23519
23520
23521 if (!text) {
23522 return '';
23523 }
23524
23525 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23526 text = text.toUpperCase();
23527 } else if (textTransform == 'lowercase') {
23528 text = text.toLowerCase();
23529 }
23530
23531 var wrapStyle = ele.pstyle('text-wrap').value;
23532
23533 if (wrapStyle === 'wrap') {
23534 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
23535
23536 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23537 return rscratch('labelWrapCachedText');
23538 }
23539
23540 var zwsp = "\u200B";
23541 var lines = text.split('\n');
23542 var maxW = ele.pstyle('text-max-width').pfValue;
23543 var overflow = ele.pstyle('text-overflow-wrap').value;
23544 var overflowAny = overflow === 'anywhere';
23545 var wrappedLines = [];
23546 var wordsRegex = /[\s\u200b]+/;
23547 var wordSeparator = overflowAny ? '' : ' ';
23548
23549 for (var l = 0; l < lines.length; l++) {
23550 var line = lines[l];
23551 var lineDims = this.calculateLabelDimensions(ele, line);
23552 var lineW = lineDims.width;
23553
23554 if (overflowAny) {
23555 var processedLine = line.split('').join(zwsp);
23556 line = processedLine;
23557 }
23558
23559 if (lineW > maxW) {
23560 // line is too long
23561 var words = line.split(wordsRegex);
23562 var subline = '';
23563
23564 for (var w = 0; w < words.length; w++) {
23565 var word = words[w];
23566 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
23567 var testDims = this.calculateLabelDimensions(ele, testLine);
23568 var testW = testDims.width;
23569
23570 if (testW <= maxW) {
23571 // word fits on current line
23572 subline += word + wordSeparator;
23573 } else {
23574 // word starts new line
23575 if (subline) {
23576 wrappedLines.push(subline);
23577 }
23578
23579 subline = word + wordSeparator;
23580 }
23581 } // if there's remaining text, put it in a wrapped line
23582
23583
23584 if (!subline.match(/^[\s\u200b]+$/)) {
23585 wrappedLines.push(subline);
23586 }
23587 } else {
23588 // line is already short enough
23589 wrappedLines.push(line);
23590 }
23591 } // for
23592
23593
23594 rscratch('labelWrapCachedLines', wrappedLines);
23595 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23596 rscratch('labelWrapKey', labelKey);
23597 } else if (wrapStyle === 'ellipsis') {
23598 var _maxW = ele.pstyle('text-max-width').pfValue;
23599 var ellipsized = '';
23600 var ellipsis = "\u2026";
23601 var incLastCh = false;
23602
23603 for (var i = 0; i < text.length; i++) {
23604 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23605
23606 if (widthWithNextCh > _maxW) {
23607 break;
23608 }
23609
23610 ellipsized += text[i];
23611
23612 if (i === text.length - 1) {
23613 incLastCh = true;
23614 }
23615 }
23616
23617 if (!incLastCh) {
23618 ellipsized += ellipsis;
23619 }
23620
23621 return ellipsized;
23622 } // if ellipsize
23623
23624
23625 return text;
23626};
23627
23628BRp$6.getLabelJustification = function (ele) {
23629 var justification = ele.pstyle('text-justification').strValue;
23630 var textHalign = ele.pstyle('text-halign').strValue;
23631
23632 if (justification === 'auto') {
23633 if (ele.isNode()) {
23634 switch (textHalign) {
23635 case 'left':
23636 return 'right';
23637
23638 case 'right':
23639 return 'left';
23640
23641 default:
23642 return 'center';
23643 }
23644 } else {
23645 return 'center';
23646 }
23647 } else {
23648 return justification;
23649 }
23650};
23651
23652BRp$6.calculateLabelDimensions = function (ele, text) {
23653 var r = this;
23654 var cacheKey = hashString(text, ele._private.labelDimsKey);
23655 var cache = r.labelDimCache || (r.labelDimCache = []);
23656 var existingVal = cache[cacheKey];
23657
23658 if (existingVal != null) {
23659 return existingVal;
23660 }
23661
23662 var sizeMult = 1; // increase the scale to increase accuracy w.r.t. zoomed text
23663
23664 var fStyle = ele.pstyle('font-style').strValue;
23665 var size = sizeMult * ele.pstyle('font-size').pfValue + 'px';
23666 var family = ele.pstyle('font-family').strValue;
23667 var weight = ele.pstyle('font-weight').strValue;
23668 var div = this.labelCalcDiv;
23669
23670 if (!div) {
23671 div = this.labelCalcDiv = document.createElement('div'); // eslint-disable-line no-undef
23672
23673 document.body.appendChild(div); // eslint-disable-line no-undef
23674 }
23675
23676 var ds = div.style; // from ele style
23677
23678 ds.fontFamily = family;
23679 ds.fontStyle = fStyle;
23680 ds.fontSize = size;
23681 ds.fontWeight = weight; // forced style
23682
23683 ds.position = 'absolute';
23684 ds.left = '-9999px';
23685 ds.top = '-9999px';
23686 ds.zIndex = '-1';
23687 ds.visibility = 'hidden';
23688 ds.pointerEvents = 'none';
23689 ds.padding = '0';
23690 ds.lineHeight = '1'; // - newlines must be taken into account for text-wrap:wrap
23691 // - since spaces are not collapsed, each space must be taken into account
23692
23693 ds.whiteSpace = 'pre'; // put label content in div
23694
23695 div.textContent = text;
23696 return cache[cacheKey] = {
23697 width: Math.ceil(div.clientWidth / sizeMult),
23698 height: Math.ceil(div.clientHeight / sizeMult)
23699 };
23700};
23701
23702BRp$6.calculateLabelAngle = function (ele, prefix) {
23703 var _p = ele._private;
23704 var rs = _p.rscratch;
23705 var isEdge = ele.isEdge();
23706 var prefixDash = prefix ? prefix + '-' : '';
23707 var rot = ele.pstyle(prefixDash + 'text-rotation');
23708 var rotStr = rot.strValue;
23709
23710 if (rotStr === 'none') {
23711 return 0;
23712 } else if (isEdge && rotStr === 'autorotate') {
23713 return rs.labelAutoAngle;
23714 } else if (rotStr === 'autorotate') {
23715 return 0;
23716 } else {
23717 return rot.pfValue;
23718 }
23719};
23720
23721BRp$6.calculateLabelAngles = function (ele) {
23722 var r = this;
23723 var isEdge = ele.isEdge();
23724 var _p = ele._private;
23725 var rs = _p.rscratch;
23726 rs.labelAngle = r.calculateLabelAngle(ele);
23727
23728 if (isEdge) {
23729 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
23730 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
23731 }
23732};
23733
23734var BRp$7 = {};
23735var TOO_SMALL_CUT_RECT = 28;
23736var warnedCutRect = false;
23737
23738BRp$7.getNodeShape = function (node) {
23739 var r = this;
23740 var shape = node.pstyle('shape').value;
23741
23742 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
23743 if (!warnedCutRect) {
23744 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
23745 warnedCutRect = true;
23746 }
23747
23748 return 'rectangle';
23749 }
23750
23751 if (node.isParent()) {
23752 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'cutrectangle' || shape === 'barrel') {
23753 return shape;
23754 } else {
23755 return 'rectangle';
23756 }
23757 }
23758
23759 if (shape === 'polygon') {
23760 var points = node.pstyle('shape-polygon-points').value;
23761 return r.nodeShapes.makePolygon(points).name;
23762 }
23763
23764 return shape;
23765};
23766
23767var BRp$8 = {};
23768
23769BRp$8.registerCalculationListeners = function () {
23770 var cy = this.cy;
23771 var elesToUpdate = cy.collection();
23772 var r = this;
23773
23774 var enqueue = function enqueue(eles) {
23775 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
23776 elesToUpdate.merge(eles);
23777
23778 if (dirtyStyleCaches) {
23779 for (var i = 0; i < eles.length; i++) {
23780 var ele = eles[i];
23781 var _p = ele._private;
23782 var rstyle = _p.rstyle;
23783 rstyle.clean = false;
23784 rstyle.cleanConnected = false;
23785 }
23786 }
23787 };
23788
23789 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
23790 var ele = e.target;
23791 enqueue(ele);
23792 }).on('style.* background.*', function onDirtyStyle(e) {
23793 var ele = e.target;
23794 enqueue(ele, false);
23795 });
23796
23797 var updateEleCalcs = function updateEleCalcs(willDraw) {
23798 if (willDraw) {
23799 var fns = r.onUpdateEleCalcsFns;
23800
23801 for (var i = 0; i < elesToUpdate.length; i++) {
23802 var ele = elesToUpdate[i];
23803 var rstyle = ele._private.rstyle;
23804
23805 if (ele.isNode() && !rstyle.cleanConnected) {
23806 enqueue(ele.connectedEdges());
23807 rstyle.cleanConnected = true;
23808 }
23809 }
23810
23811 if (fns) {
23812 for (var _i = 0; _i < fns.length; _i++) {
23813 var fn = fns[_i];
23814 fn(willDraw, elesToUpdate);
23815 }
23816 }
23817
23818 r.recalculateRenderedStyle(elesToUpdate);
23819 elesToUpdate = cy.collection();
23820 }
23821 };
23822
23823 r.flushRenderedStyleQueue = function () {
23824 updateEleCalcs(true);
23825 };
23826
23827 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
23828};
23829
23830BRp$8.onUpdateEleCalcs = function (fn) {
23831 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
23832 fns.push(fn);
23833};
23834
23835BRp$8.recalculateRenderedStyle = function (eles, useCache) {
23836 var isCleanConnected = function isCleanConnected(ele) {
23837 return ele._private.rstyle.cleanConnected;
23838 };
23839
23840 var edges = [];
23841 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
23842
23843 if (this.destroyed) {
23844 return;
23845 } // use cache by default for perf
23846
23847
23848 if (useCache === undefined) {
23849 useCache = true;
23850 }
23851
23852 for (var i = 0; i < eles.length; i++) {
23853 var ele = eles[i];
23854 var _p = ele._private;
23855 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
23856 // (and a request for recalc may come in between frames)
23857
23858 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
23859 rstyle.clean = false;
23860 } // only update if dirty and in graph
23861
23862
23863 if (useCache && rstyle.clean || ele.removed()) {
23864 continue;
23865 } // only update if not display: none
23866
23867
23868 if (ele.pstyle('display').value === 'none') {
23869 continue;
23870 }
23871
23872 if (_p.group === 'nodes') {
23873 nodes.push(ele);
23874 } else {
23875 // edges
23876 edges.push(ele);
23877 }
23878
23879 rstyle.clean = true;
23880 } // update node data from projections
23881
23882
23883 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
23884 var _ele = nodes[_i2];
23885 var _p2 = _ele._private;
23886 var _rstyle = _p2.rstyle;
23887
23888 var pos = _ele.position();
23889
23890 this.recalculateNodeLabelProjection(_ele);
23891 _rstyle.nodeX = pos.x;
23892 _rstyle.nodeY = pos.y;
23893 _rstyle.nodeW = _ele.pstyle('width').pfValue;
23894 _rstyle.nodeH = _ele.pstyle('height').pfValue;
23895 }
23896
23897 this.recalculateEdgeProjections(edges); // update edge data from projections
23898
23899 for (var _i3 = 0; _i3 < edges.length; _i3++) {
23900 var _ele2 = edges[_i3];
23901 var _p3 = _ele2._private;
23902 var _rstyle2 = _p3.rstyle;
23903 var rs = _p3.rscratch; // update rstyle positions
23904
23905 _rstyle2.srcX = rs.arrowStartX;
23906 _rstyle2.srcY = rs.arrowStartY;
23907 _rstyle2.tgtX = rs.arrowEndX;
23908 _rstyle2.tgtY = rs.arrowEndY;
23909 _rstyle2.midX = rs.midX;
23910 _rstyle2.midY = rs.midY;
23911 _rstyle2.labelAngle = rs.labelAngle;
23912 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
23913 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
23914 }
23915};
23916
23917var BRp$9 = {};
23918
23919BRp$9.updateCachedGrabbedEles = function () {
23920 var eles = this.cachedZSortedEles;
23921
23922 if (!eles) {
23923 // just let this be recalculated on the next z sort tick
23924 return;
23925 }
23926
23927 eles.drag = [];
23928 eles.nondrag = [];
23929 var grabTargets = [];
23930
23931 for (var i = 0; i < eles.length; i++) {
23932 var ele = eles[i];
23933 var rs = ele._private.rscratch;
23934
23935 if (ele.grabbed() && !ele.isParent()) {
23936 grabTargets.push(ele);
23937 } else if (rs.inDragLayer) {
23938 eles.drag.push(ele);
23939 } else {
23940 eles.nondrag.push(ele);
23941 }
23942 } // put the grab target nodes last so it's on top of its neighbourhood
23943
23944
23945 for (var i = 0; i < grabTargets.length; i++) {
23946 var ele = grabTargets[i];
23947 eles.drag.push(ele);
23948 }
23949};
23950
23951BRp$9.invalidateCachedZSortedEles = function () {
23952 this.cachedZSortedEles = null;
23953};
23954
23955BRp$9.getCachedZSortedEles = function (forceRecalc) {
23956 if (forceRecalc || !this.cachedZSortedEles) {
23957 var eles = this.cy.mutableElements().toArray();
23958 eles.sort(zIndexSort);
23959 eles.interactive = eles.filter(function (ele) {
23960 return ele.interactive();
23961 });
23962 this.cachedZSortedEles = eles;
23963 this.updateCachedGrabbedEles();
23964 } else {
23965 eles = this.cachedZSortedEles;
23966 }
23967
23968 return eles;
23969};
23970
23971var BRp$a = {};
23972[BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
23973 extend(BRp$a, props);
23974});
23975
23976var BRp$b = {};
23977
23978BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
23979 var r = this;
23980 var imageCache = r.imageCache = r.imageCache || {};
23981 var cache = imageCache[url];
23982
23983 if (cache) {
23984 if (!cache.image.complete) {
23985 cache.image.addEventListener('load', onLoad);
23986 }
23987
23988 return cache.image;
23989 } else {
23990 cache = imageCache[url] = imageCache[url] || {};
23991 var image = cache.image = new Image(); // eslint-disable-line no-undef
23992
23993 image.addEventListener('load', onLoad);
23994 image.addEventListener('error', function () {
23995 image.error = true;
23996 }); // #1582 safari doesn't load data uris with crossOrigin properly
23997 // https://bugs.webkit.org/show_bug.cgi?id=123978
23998
23999 var dataUriPrefix = 'data:';
24000 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24001
24002 if (!isDataUri) {
24003 image.crossOrigin = crossOrigin; // prevent tainted canvas
24004 }
24005
24006 image.src = url;
24007 return image;
24008 }
24009};
24010
24011var BRp$c = {};
24012/* global document, window, ResizeObserver, MutationObserver */
24013
24014BRp$c.registerBinding = function (target, event, handler, useCapture) {
24015 // eslint-disable-line no-unused-vars
24016 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24017
24018 var b = this.binder(target);
24019 return b.on.apply(b, args);
24020};
24021
24022BRp$c.binder = function (tgt) {
24023 var r = this;
24024 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24025
24026 if (r.supportsPassiveEvents == null) {
24027 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24028 var supportsPassive = false;
24029
24030 try {
24031 var opts = Object.defineProperty({}, 'passive', {
24032 get: function get() {
24033 supportsPassive = true;
24034 return true;
24035 }
24036 });
24037 window.addEventListener('test', null, opts);
24038 } catch (err) {// not supported
24039 }
24040
24041 r.supportsPassiveEvents = supportsPassive;
24042 }
24043
24044 var on = function on(event, handler, useCapture) {
24045 var args = Array.prototype.slice.call(arguments);
24046
24047 if (tgtIsDom && r.supportsPassiveEvents) {
24048 // replace useCapture w/ opts obj
24049 args[2] = {
24050 capture: useCapture != null ? useCapture : false,
24051 passive: false,
24052 once: false
24053 };
24054 }
24055
24056 r.bindings.push({
24057 target: tgt,
24058 args: args
24059 });
24060 (tgt.addEventListener || tgt.on).apply(tgt, args);
24061 return this;
24062 };
24063
24064 return {
24065 on: on,
24066 addEventListener: on,
24067 addListener: on,
24068 bind: on
24069 };
24070};
24071
24072BRp$c.nodeIsDraggable = function (node) {
24073 return node && node.isNode() && !node.locked() && node.grabbable();
24074};
24075
24076BRp$c.nodeIsGrabbable = function (node) {
24077 return this.nodeIsDraggable(node) && node.interactive();
24078};
24079
24080BRp$c.load = function () {
24081 var r = this;
24082
24083 var isSelected = function isSelected(ele) {
24084 return ele.selected();
24085 };
24086
24087 var triggerEvents = function triggerEvents(target, names, e, position) {
24088 if (target == null) {
24089 target = r.cy;
24090 }
24091
24092 for (var i = 0; i < names.length; i++) {
24093 var name = names[i];
24094 target.emit({
24095 originalEvent: e,
24096 type: name,
24097 position: position
24098 });
24099 }
24100 };
24101
24102 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24103 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24104 };
24105
24106 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24107 var allowPassthrough = true;
24108
24109 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24110 // a grabbable compound node below the ele => no passthrough panning
24111 for (var i = 0; downs && i < downs.length; i++) {
24112 var down = downs[i];
24113
24114 if (down.isNode() && down.isParent()) {
24115 allowPassthrough = false;
24116 break;
24117 }
24118 }
24119 } else {
24120 allowPassthrough = true;
24121 }
24122
24123 return allowPassthrough;
24124 };
24125
24126 var setGrabbed = function setGrabbed(ele) {
24127 ele[0]._private.grabbed = true;
24128 };
24129
24130 var setFreed = function setFreed(ele) {
24131 ele[0]._private.grabbed = false;
24132 };
24133
24134 var setInDragLayer = function setInDragLayer(ele) {
24135 ele[0]._private.rscratch.inDragLayer = true;
24136 };
24137
24138 var setOutDragLayer = function setOutDragLayer(ele) {
24139 ele[0]._private.rscratch.inDragLayer = false;
24140 };
24141
24142 var setGrabTarget = function setGrabTarget(ele) {
24143 ele[0]._private.rscratch.isGrabTarget = true;
24144 };
24145
24146 var removeGrabTarget = function removeGrabTarget(ele) {
24147 ele[0]._private.rscratch.isGrabTarget = false;
24148 };
24149
24150 var addToDragList = function addToDragList(ele, opts) {
24151 var list = opts.addToList;
24152 var listHasEle = list.has(ele);
24153
24154 if (!listHasEle) {
24155 list.merge(ele);
24156 setGrabbed(ele);
24157 }
24158 }; // helper function to determine which child nodes and inner edges
24159 // of a compound node to be dragged as well as the grabbed and selected nodes
24160
24161
24162 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24163 if (!node.cy().hasCompoundNodes()) {
24164 return;
24165 }
24166
24167 if (opts.inDragLayer == null && opts.addToList == null) {
24168 return;
24169 } // nothing to do
24170
24171
24172 var innerNodes = node.descendants();
24173
24174 if (opts.inDragLayer) {
24175 innerNodes.forEach(setInDragLayer);
24176 innerNodes.connectedEdges().forEach(setInDragLayer);
24177 }
24178
24179 if (opts.addToList) {
24180 opts.addToList.unmerge(innerNodes);
24181 }
24182 }; // adds the given nodes and its neighbourhood to the drag layer
24183
24184
24185 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24186 opts = opts || {};
24187 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24188
24189 if (opts.inDragLayer) {
24190 nodes.forEach(setInDragLayer);
24191 nodes.neighborhood().stdFilter(function (ele) {
24192 return !hasCompoundNodes || ele.isEdge();
24193 }).forEach(setInDragLayer);
24194 }
24195
24196 if (opts.addToList) {
24197 nodes.forEach(function (ele) {
24198 addToDragList(ele, opts);
24199 });
24200 }
24201
24202 addDescendantsToDrag(nodes, opts); // always add to drag
24203 // also add nodes and edges related to the topmost ancestor
24204
24205 updateAncestorsInDragLayer(nodes, {
24206 inDragLayer: opts.inDragLayer
24207 });
24208 r.updateCachedGrabbedEles();
24209 };
24210
24211 var addNodeToDrag = addNodesToDrag;
24212
24213 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24214 if (!grabbedEles) {
24215 return;
24216 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24217
24218
24219 r.getCachedZSortedEles().forEach(function (ele) {
24220 setFreed(ele);
24221 setOutDragLayer(ele);
24222 removeGrabTarget(ele);
24223 });
24224 r.updateCachedGrabbedEles();
24225 }; // helper function to determine which ancestor nodes and edges should go
24226 // to the drag layer (or should be removed from drag layer).
24227
24228
24229 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24230 if (opts.inDragLayer == null && opts.addToList == null) {
24231 return;
24232 } // nothing to do
24233
24234
24235 if (!node.cy().hasCompoundNodes()) {
24236 return;
24237 } // find top-level parent
24238
24239
24240 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
24241
24242 if (parent.same(node)) {
24243 return;
24244 }
24245
24246 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24247 var edges = nodes.connectedEdges();
24248
24249 if (opts.inDragLayer) {
24250 edges.forEach(setInDragLayer);
24251 nodes.forEach(setInDragLayer);
24252 }
24253
24254 if (opts.addToList) {
24255 nodes.forEach(function (ele) {
24256 addToDragList(ele, opts);
24257 });
24258 }
24259 };
24260
24261 var blurActiveDomElement = function blurActiveDomElement() {
24262 if (document.activeElement != null && document.activeElement.blur != null) {
24263 document.activeElement.blur();
24264 }
24265 };
24266
24267 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24268 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
24269
24270 if (haveMutationsApi) {
24271 r.removeObserver = new MutationObserver(function (mutns) {
24272 // eslint-disable-line no-undef
24273 for (var i = 0; i < mutns.length; i++) {
24274 var mutn = mutns[i];
24275 var rNodes = mutn.removedNodes;
24276
24277 if (rNodes) {
24278 for (var j = 0; j < rNodes.length; j++) {
24279 var rNode = rNodes[j];
24280
24281 if (rNode === r.container) {
24282 r.destroy();
24283 break;
24284 }
24285 }
24286 }
24287 }
24288 });
24289
24290 if (r.container.parentNode) {
24291 r.removeObserver.observe(r.container.parentNode, {
24292 childList: true
24293 });
24294 }
24295 } else {
24296 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24297 // eslint-disable-line no-unused-vars
24298 r.destroy();
24299 });
24300 }
24301
24302 var onResize = util(function () {
24303 r.cy.resize();
24304 }, 100);
24305
24306 if (haveMutationsApi) {
24307 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24308
24309 r.styleObserver.observe(r.container, {
24310 attributes: true
24311 });
24312 } // auto resize
24313
24314
24315 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
24316
24317 if (haveResizeObserverApi) {
24318 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24319
24320 r.resizeObserver.observe(r.container);
24321 }
24322
24323 var forEachUp = function forEachUp(domEle, fn) {
24324 while (domEle != null) {
24325 fn(domEle);
24326 domEle = domEle.parentNode;
24327 }
24328 };
24329
24330 var invalidateCoords = function invalidateCoords() {
24331 r.invalidateContainerClientCoordsCache();
24332 };
24333
24334 forEachUp(r.container, function (domEle) {
24335 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24336 r.registerBinding(domEle, 'animationend', invalidateCoords);
24337 r.registerBinding(domEle, 'scroll', invalidateCoords);
24338 }); // stop right click menu from appearing on cy
24339
24340 r.registerBinding(r.container, 'contextmenu', function (e) {
24341 e.preventDefault();
24342 });
24343
24344 var inBoxSelection = function inBoxSelection() {
24345 return r.selection[4] !== 0;
24346 };
24347
24348 var eventInContainer = function eventInContainer(e) {
24349 // save cycles if mouse events aren't to be captured
24350 var containerPageCoords = r.findContainerClientCoords();
24351 var x = containerPageCoords[0];
24352 var y = containerPageCoords[1];
24353 var width = containerPageCoords[2];
24354 var height = containerPageCoords[3];
24355 var positions = e.touches ? e.touches : [e];
24356 var atLeastOnePosInside = false;
24357
24358 for (var i = 0; i < positions.length; i++) {
24359 var p = positions[i];
24360
24361 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24362 atLeastOnePosInside = true;
24363 break;
24364 }
24365 }
24366
24367 if (!atLeastOnePosInside) {
24368 return false;
24369 }
24370
24371 var container = r.container;
24372 var target = e.target;
24373 var tParent = target.parentNode;
24374 var containerIsTarget = false;
24375
24376 while (tParent) {
24377 if (tParent === container) {
24378 containerIsTarget = true;
24379 break;
24380 }
24381
24382 tParent = tParent.parentNode;
24383 }
24384
24385 if (!containerIsTarget) {
24386 return false;
24387 } // if target is outisde cy container, then this event is not for us
24388
24389
24390 return true;
24391 }; // Primary key
24392
24393
24394 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24395 if (!eventInContainer(e)) {
24396 return;
24397 }
24398
24399 e.preventDefault();
24400 blurActiveDomElement();
24401 r.hoverData.capture = true;
24402 r.hoverData.which = e.which;
24403 var cy = r.cy;
24404 var gpos = [e.clientX, e.clientY];
24405 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24406 var select = r.selection;
24407 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24408 var near = nears[0];
24409 var draggedElements = r.dragData.possibleDragElements;
24410 r.hoverData.mdownPos = pos;
24411 r.hoverData.mdownGPos = gpos;
24412
24413 var checkForTaphold = function checkForTaphold() {
24414 r.hoverData.tapholdCancelled = false;
24415 clearTimeout(r.hoverData.tapholdTimeout);
24416 r.hoverData.tapholdTimeout = setTimeout(function () {
24417 if (r.hoverData.tapholdCancelled) {
24418 return;
24419 } else {
24420 var ele = r.hoverData.down;
24421
24422 if (ele) {
24423 ele.emit({
24424 originalEvent: e,
24425 type: 'taphold',
24426 position: {
24427 x: pos[0],
24428 y: pos[1]
24429 }
24430 });
24431 } else {
24432 cy.emit({
24433 originalEvent: e,
24434 type: 'taphold',
24435 position: {
24436 x: pos[0],
24437 y: pos[1]
24438 }
24439 });
24440 }
24441 }
24442 }, r.tapholdDuration);
24443 }; // Right click button
24444
24445
24446 if (e.which == 3) {
24447 r.hoverData.cxtStarted = true;
24448 var cxtEvt = {
24449 originalEvent: e,
24450 type: 'cxttapstart',
24451 position: {
24452 x: pos[0],
24453 y: pos[1]
24454 }
24455 };
24456
24457 if (near) {
24458 near.activate();
24459 near.emit(cxtEvt);
24460 r.hoverData.down = near;
24461 } else {
24462 cy.emit(cxtEvt);
24463 }
24464
24465 r.hoverData.downTime = new Date().getTime();
24466 r.hoverData.cxtDragged = false; // Primary button
24467 } else if (e.which == 1) {
24468 if (near) {
24469 near.activate();
24470 } // Element dragging
24471
24472
24473 {
24474 // If something is under the cursor and it is draggable, prepare to grab it
24475 if (near != null) {
24476 if (r.nodeIsGrabbable(near)) {
24477 var makeEvent = function makeEvent(type) {
24478 return {
24479 originalEvent: e,
24480 type: type,
24481 position: {
24482 x: pos[0],
24483 y: pos[1]
24484 }
24485 };
24486 };
24487
24488 var triggerGrab = function triggerGrab(ele) {
24489 ele.emit(makeEvent('grab'));
24490 };
24491
24492 setGrabTarget(near);
24493
24494 if (!near.selected()) {
24495 draggedElements = r.dragData.possibleDragElements = cy.collection();
24496 addNodeToDrag(near, {
24497 addToList: draggedElements
24498 });
24499 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24500 } else {
24501 draggedElements = r.dragData.possibleDragElements = cy.collection();
24502 var selectedNodes = cy.$(function (ele) {
24503 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24504 });
24505 addNodesToDrag(selectedNodes, {
24506 addToList: draggedElements
24507 });
24508 near.emit(makeEvent('grabon'));
24509 selectedNodes.forEach(triggerGrab);
24510 }
24511
24512 r.redrawHint('eles', true);
24513 r.redrawHint('drag', true);
24514 }
24515 }
24516
24517 r.hoverData.down = near;
24518 r.hoverData.downs = nears;
24519 r.hoverData.downTime = new Date().getTime();
24520 }
24521 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24522 x: pos[0],
24523 y: pos[1]
24524 });
24525
24526 if (near == null) {
24527 select[4] = 1;
24528 r.data.bgActivePosistion = {
24529 x: pos[0],
24530 y: pos[1]
24531 };
24532 r.redrawHint('select', true);
24533 r.redraw();
24534 } else if (near.pannable()) {
24535 select[4] = 1; // for future pan
24536 }
24537
24538 checkForTaphold();
24539 } // Initialize selection box coordinates
24540
24541
24542 select[0] = select[2] = pos[0];
24543 select[1] = select[3] = pos[1];
24544 }, false);
24545 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
24546 // eslint-disable-line no-undef
24547 var capture = r.hoverData.capture;
24548
24549 if (!capture && !eventInContainer(e)) {
24550 return;
24551 }
24552
24553 var preventDefault = false;
24554 var cy = r.cy;
24555 var zoom = cy.zoom();
24556 var gpos = [e.clientX, e.clientY];
24557 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24558 var mdownPos = r.hoverData.mdownPos;
24559 var mdownGPos = r.hoverData.mdownGPos;
24560 var select = r.selection;
24561 var near = null;
24562
24563 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24564 near = r.findNearestElement(pos[0], pos[1], true, false);
24565 }
24566
24567 var last = r.hoverData.last;
24568 var down = r.hoverData.down;
24569 var disp = [pos[0] - select[2], pos[1] - select[3]];
24570 var draggedElements = r.dragData.possibleDragElements;
24571 var isOverThresholdDrag;
24572
24573 if (mdownGPos) {
24574 var dx = gpos[0] - mdownGPos[0];
24575 var dx2 = dx * dx;
24576 var dy = gpos[1] - mdownGPos[1];
24577 var dy2 = dy * dy;
24578 var dist2 = dx2 + dy2;
24579 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24580 }
24581
24582 var multSelKeyDown = isMultSelKeyDown(e);
24583
24584 if (isOverThresholdDrag) {
24585 r.hoverData.tapholdCancelled = true;
24586 }
24587
24588 var updateDragDelta = function updateDragDelta() {
24589 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24590
24591 if (dragDelta.length === 0) {
24592 dragDelta.push(disp[0]);
24593 dragDelta.push(disp[1]);
24594 } else {
24595 dragDelta[0] += disp[0];
24596 dragDelta[1] += disp[1];
24597 }
24598 };
24599
24600 preventDefault = true;
24601 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24602 x: pos[0],
24603 y: pos[1]
24604 });
24605
24606 var goIntoBoxMode = function goIntoBoxMode() {
24607 r.data.bgActivePosistion = undefined;
24608
24609 if (!r.hoverData.selecting) {
24610 cy.emit({
24611 originalEvent: e,
24612 type: 'boxstart',
24613 position: {
24614 x: pos[0],
24615 y: pos[1]
24616 }
24617 });
24618 }
24619
24620 select[4] = 1;
24621 r.hoverData.selecting = true;
24622 r.redrawHint('select', true);
24623 r.redraw();
24624 }; // trigger context drag if rmouse down
24625
24626
24627 if (r.hoverData.which === 3) {
24628 // but only if over threshold
24629 if (isOverThresholdDrag) {
24630 var cxtEvt = {
24631 originalEvent: e,
24632 type: 'cxtdrag',
24633 position: {
24634 x: pos[0],
24635 y: pos[1]
24636 }
24637 };
24638
24639 if (down) {
24640 down.emit(cxtEvt);
24641 } else {
24642 cy.emit(cxtEvt);
24643 }
24644
24645 r.hoverData.cxtDragged = true;
24646
24647 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24648 if (r.hoverData.cxtOver) {
24649 r.hoverData.cxtOver.emit({
24650 originalEvent: e,
24651 type: 'cxtdragout',
24652 position: {
24653 x: pos[0],
24654 y: pos[1]
24655 }
24656 });
24657 }
24658
24659 r.hoverData.cxtOver = near;
24660
24661 if (near) {
24662 near.emit({
24663 originalEvent: e,
24664 type: 'cxtdragover',
24665 position: {
24666 x: pos[0],
24667 y: pos[1]
24668 }
24669 });
24670 }
24671 }
24672 } // Check if we are drag panning the entire graph
24673
24674 } else if (r.hoverData.dragging) {
24675 preventDefault = true;
24676
24677 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24678 var deltaP;
24679
24680 if (r.hoverData.justStartedPan) {
24681 var mdPos = r.hoverData.mdownPos;
24682 deltaP = {
24683 x: (pos[0] - mdPos[0]) * zoom,
24684 y: (pos[1] - mdPos[1]) * zoom
24685 };
24686 r.hoverData.justStartedPan = false;
24687 } else {
24688 deltaP = {
24689 x: disp[0] * zoom,
24690 y: disp[1] * zoom
24691 };
24692 }
24693
24694 cy.panBy(deltaP);
24695 r.hoverData.dragged = true;
24696 } // Needs reproject due to pan changing viewport
24697
24698
24699 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24700 } else if (select[4] == 1 && (down == null || down.pannable())) {
24701 if (isOverThresholdDrag) {
24702 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24703 goIntoBoxMode();
24704 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24705 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24706
24707 if (allowPassthrough) {
24708 r.hoverData.dragging = true;
24709 r.hoverData.justStartedPan = true;
24710 select[4] = 0;
24711 r.data.bgActivePosistion = array2point(mdownPos);
24712 r.redrawHint('select', true);
24713 r.redraw();
24714 }
24715 }
24716
24717 if (down && down.pannable() && down.active()) {
24718 down.unactivate();
24719 }
24720 }
24721 } else {
24722 if (down && down.pannable() && down.active()) {
24723 down.unactivate();
24724 }
24725
24726 if ((!down || !down.grabbed()) && near != last) {
24727 if (last) {
24728 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24729 x: pos[0],
24730 y: pos[1]
24731 });
24732 }
24733
24734 if (near) {
24735 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24736 x: pos[0],
24737 y: pos[1]
24738 });
24739 }
24740
24741 r.hoverData.last = near;
24742 }
24743
24744 if (down) {
24745 if (isOverThresholdDrag) {
24746 // then we can take action
24747 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24748 // then selection overrides
24749 if (down && down.grabbed()) {
24750 freeDraggedElements(draggedElements);
24751 down.emit('freeon');
24752 draggedElements.emit('free');
24753
24754 if (r.dragData.didDrag) {
24755 down.emit('dragfreeon');
24756 draggedElements.emit('dragfree');
24757 }
24758 }
24759
24760 goIntoBoxMode();
24761 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24762 // drag node
24763 var justStartedDrag = !r.dragData.didDrag;
24764
24765 if (justStartedDrag) {
24766 r.redrawHint('eles', true);
24767 }
24768
24769 r.dragData.didDrag = true; // indicate that we actually did drag the node
24770
24771 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
24772
24773 if (!r.hoverData.draggingEles) {
24774 addNodesToDrag(draggedElements, {
24775 inDragLayer: true
24776 });
24777 }
24778
24779 var totalShift = {
24780 x: 0,
24781 y: 0
24782 };
24783
24784 if (number(disp[0]) && number(disp[1])) {
24785 totalShift.x += disp[0];
24786 totalShift.y += disp[1];
24787
24788 if (justStartedDrag) {
24789 var dragDelta = r.hoverData.dragDelta;
24790
24791 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
24792 totalShift.x += dragDelta[0];
24793 totalShift.y += dragDelta[1];
24794 }
24795 }
24796 }
24797
24798 for (var i = 0; i < draggedElements.length; i++) {
24799 var dEle = draggedElements[i];
24800
24801 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
24802 toTrigger.merge(dEle);
24803 }
24804 }
24805
24806 r.hoverData.draggingEles = true;
24807 toTrigger.silentShift(totalShift).emit('position drag');
24808 r.redrawHint('drag', true);
24809 r.redraw();
24810 }
24811 } else {
24812 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
24813 updateDragDelta();
24814 }
24815 } // prevent the dragging from triggering text selection on the page
24816
24817
24818 preventDefault = true;
24819 }
24820
24821 select[2] = pos[0];
24822 select[3] = pos[1];
24823
24824 if (preventDefault) {
24825 if (e.stopPropagation) e.stopPropagation();
24826 if (e.preventDefault) e.preventDefault();
24827 return false;
24828 }
24829 }, false);
24830 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
24831 // eslint-disable-line no-undef
24832 var capture = r.hoverData.capture;
24833
24834 if (!capture) {
24835 return;
24836 }
24837
24838 r.hoverData.capture = false;
24839 var cy = r.cy;
24840 var pos = r.projectIntoViewport(e.clientX, e.clientY);
24841 var select = r.selection;
24842 var near = r.findNearestElement(pos[0], pos[1], true, false);
24843 var draggedElements = r.dragData.possibleDragElements;
24844 var down = r.hoverData.down;
24845 var multSelKeyDown = isMultSelKeyDown(e);
24846
24847 if (r.data.bgActivePosistion) {
24848 r.redrawHint('select', true);
24849 r.redraw();
24850 }
24851
24852 r.hoverData.tapholdCancelled = true;
24853 r.data.bgActivePosistion = undefined; // not active bg now
24854
24855 if (down) {
24856 down.unactivate();
24857 }
24858
24859 if (r.hoverData.which === 3) {
24860 var cxtEvt = {
24861 originalEvent: e,
24862 type: 'cxttapend',
24863 position: {
24864 x: pos[0],
24865 y: pos[1]
24866 }
24867 };
24868
24869 if (down) {
24870 down.emit(cxtEvt);
24871 } else {
24872 cy.emit(cxtEvt);
24873 }
24874
24875 if (!r.hoverData.cxtDragged) {
24876 var cxtTap = {
24877 originalEvent: e,
24878 type: 'cxttap',
24879 position: {
24880 x: pos[0],
24881 y: pos[1]
24882 }
24883 };
24884
24885 if (down) {
24886 down.emit(cxtTap);
24887 } else {
24888 cy.emit(cxtTap);
24889 }
24890 }
24891
24892 r.hoverData.cxtDragged = false;
24893 r.hoverData.which = null;
24894 } else if (r.hoverData.which === 1) {
24895 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
24896 x: pos[0],
24897 y: pos[1]
24898 });
24899
24900 if (!r.dragData.didDrag // didn't move a node around
24901 && !r.hoverData.dragged // didn't pan
24902 && !r.hoverData.selecting // not box selection
24903 && !r.hoverData.isOverThresholdDrag // didn't move too much
24904 ) {
24905 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
24906 x: pos[0],
24907 y: pos[1]
24908 });
24909 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
24910
24911
24912 if (down == null && // not mousedown on node
24913 !r.dragData.didDrag // didn't move the node around
24914 && !r.hoverData.selecting // not box selection
24915 && !r.hoverData.dragged // didn't pan
24916 && !isMultSelKeyDown(e)) {
24917 cy.$(isSelected).unselect(['tapunselect']);
24918
24919 if (draggedElements.length > 0) {
24920 r.redrawHint('eles', true);
24921 }
24922
24923 r.dragData.possibleDragElements = draggedElements = cy.collection();
24924 } // Single selection
24925
24926
24927 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
24928 if (near != null && near._private.selectable) {
24929 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
24930 if (near.selected()) {
24931 near.unselect(['tapunselect']);
24932 } else {
24933 near.select(['tapselect']);
24934 }
24935 } else {
24936 if (!multSelKeyDown) {
24937 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
24938 near.select(['tapselect']);
24939 }
24940 }
24941
24942 r.redrawHint('eles', true);
24943 }
24944 }
24945
24946 if (r.hoverData.selecting) {
24947 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
24948 r.redrawHint('select', true);
24949
24950 if (box.length > 0) {
24951 r.redrawHint('eles', true);
24952 }
24953
24954 cy.emit({
24955 type: 'boxend',
24956 originalEvent: e,
24957 position: {
24958 x: pos[0],
24959 y: pos[1]
24960 }
24961 });
24962
24963 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
24964 return ele.selectable() && !ele.selected();
24965 };
24966
24967 if (cy.selectionType() === 'additive') {
24968 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
24969 } else {
24970 if (!multSelKeyDown) {
24971 cy.$(isSelected).unmerge(box).unselect();
24972 }
24973
24974 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
24975 } // always need redraw in case eles unselectable
24976
24977
24978 r.redraw();
24979 } // Cancel drag pan
24980
24981
24982 if (r.hoverData.dragging) {
24983 r.hoverData.dragging = false;
24984 r.redrawHint('select', true);
24985 r.redrawHint('eles', true);
24986 r.redraw();
24987 }
24988
24989 if (!select[4]) {
24990 r.redrawHint('drag', true);
24991 r.redrawHint('eles', true);
24992 var downWasGrabbed = down && down.grabbed();
24993 freeDraggedElements(draggedElements);
24994
24995 if (downWasGrabbed) {
24996 down.emit('freeon');
24997 draggedElements.emit('free');
24998
24999 if (r.dragData.didDrag) {
25000 down.emit('dragfreeon');
25001 draggedElements.emit('dragfree');
25002 }
25003 }
25004 }
25005 } // else not right mouse
25006
25007
25008 select[4] = 0;
25009 r.hoverData.down = null;
25010 r.hoverData.cxtStarted = false;
25011 r.hoverData.draggingEles = false;
25012 r.hoverData.selecting = false;
25013 r.hoverData.isOverThresholdDrag = false;
25014 r.dragData.didDrag = false;
25015 r.hoverData.dragged = false;
25016 r.hoverData.dragDelta = [];
25017 r.hoverData.mdownPos = null;
25018 r.hoverData.mdownGPos = null;
25019 }, false);
25020
25021 var wheelHandler = function wheelHandler(e) {
25022 if (r.scrollingPage) {
25023 return;
25024 } // while scrolling, ignore wheel-to-zoom
25025
25026
25027 var cy = r.cy;
25028 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25029 var rpos = [pos[0] * cy.zoom() + cy.pan().x, pos[1] * cy.zoom() + cy.pan().y];
25030
25031 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25032 // if pan dragging or cxt dragging, wheel movements make no zoom
25033 e.preventDefault();
25034 return;
25035 }
25036
25037 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25038 e.preventDefault();
25039 r.data.wheelZooming = true;
25040 clearTimeout(r.data.wheelTimeout);
25041 r.data.wheelTimeout = setTimeout(function () {
25042 r.data.wheelZooming = false;
25043 r.redrawHint('eles', true);
25044 r.redraw();
25045 }, 150);
25046 var diff;
25047
25048 if (e.deltaY != null) {
25049 diff = e.deltaY / -250;
25050 } else if (e.wheelDeltaY != null) {
25051 diff = e.wheelDeltaY / 1000;
25052 } else {
25053 diff = e.wheelDelta / 1000;
25054 }
25055
25056 diff = diff * r.wheelSensitivity;
25057 var needsWheelFix = e.deltaMode === 1;
25058
25059 if (needsWheelFix) {
25060 // fixes slow wheel events on ff/linux and ff/windows
25061 diff *= 33;
25062 }
25063
25064 cy.zoom({
25065 level: cy.zoom() * Math.pow(10, diff),
25066 renderedPosition: {
25067 x: rpos[0],
25068 y: rpos[1]
25069 }
25070 });
25071 }
25072 }; // Functions to help with whether mouse wheel should trigger zooming
25073 // --
25074
25075
25076 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25077 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25078 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25079 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25080
25081 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25082 // eslint-disable-line no-unused-vars
25083 r.scrollingPage = true;
25084 clearTimeout(r.scrollingPageTimeout);
25085 r.scrollingPageTimeout = setTimeout(function () {
25086 r.scrollingPage = false;
25087 }, 250);
25088 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25089 // Handle mouseout on Cytoscape container
25090
25091 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25092 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25093 r.cy.emit({
25094 originalEvent: e,
25095 type: 'mouseout',
25096 position: {
25097 x: pos[0],
25098 y: pos[1]
25099 }
25100 });
25101 }, false);
25102 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25103 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25104 r.cy.emit({
25105 originalEvent: e,
25106 type: 'mouseover',
25107 position: {
25108 x: pos[0],
25109 y: pos[1]
25110 }
25111 });
25112 }, false);
25113 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25114
25115 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25116
25117 var center1, modelCenter1; // center point on start pinch to zoom
25118
25119 var offsetLeft, offsetTop;
25120 var containerWidth, containerHeight;
25121 var twoFingersStartInside;
25122
25123 var distance = function distance(x1, y1, x2, y2) {
25124 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25125 };
25126
25127 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25128 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25129 };
25130
25131 var touchstartHandler;
25132 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25133 if (!eventInContainer(e)) {
25134 return;
25135 }
25136
25137 blurActiveDomElement();
25138 r.touchData.capture = true;
25139 r.data.bgActivePosistion = undefined;
25140 var cy = r.cy;
25141 var now = r.touchData.now;
25142 var earlier = r.touchData.earlier;
25143
25144 if (e.touches[0]) {
25145 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25146 now[0] = pos[0];
25147 now[1] = pos[1];
25148 }
25149
25150 if (e.touches[1]) {
25151 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25152 now[2] = pos[0];
25153 now[3] = pos[1];
25154 }
25155
25156 if (e.touches[2]) {
25157 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25158 now[4] = pos[0];
25159 now[5] = pos[1];
25160 } // record starting points for pinch-to-zoom
25161
25162
25163 if (e.touches[1]) {
25164 r.touchData.singleTouchMoved = true;
25165 freeDraggedElements(r.dragData.touchDragEles);
25166 var offsets = r.findContainerClientCoords();
25167 offsetLeft = offsets[0];
25168 offsetTop = offsets[1];
25169 containerWidth = offsets[2];
25170 containerHeight = offsets[3];
25171 f1x1 = e.touches[0].clientX - offsetLeft;
25172 f1y1 = e.touches[0].clientY - offsetTop;
25173 f2x1 = e.touches[1].clientX - offsetLeft;
25174 f2y1 = e.touches[1].clientY - offsetTop;
25175 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25176 var pan = cy.pan();
25177 var zoom = cy.zoom();
25178 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25179 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25180 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25181 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25182
25183 var cxtDistThreshold = 200;
25184 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25185
25186 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25187 var near1 = r.findNearestElement(now[0], now[1], true, true);
25188 var near2 = r.findNearestElement(now[2], now[3], true, true);
25189
25190 if (near1 && near1.isNode()) {
25191 near1.activate().emit({
25192 originalEvent: e,
25193 type: 'cxttapstart',
25194 position: {
25195 x: now[0],
25196 y: now[1]
25197 }
25198 });
25199 r.touchData.start = near1;
25200 } else if (near2 && near2.isNode()) {
25201 near2.activate().emit({
25202 originalEvent: e,
25203 type: 'cxttapstart',
25204 position: {
25205 x: now[0],
25206 y: now[1]
25207 }
25208 });
25209 r.touchData.start = near2;
25210 } else {
25211 cy.emit({
25212 originalEvent: e,
25213 type: 'cxttapstart',
25214 position: {
25215 x: now[0],
25216 y: now[1]
25217 }
25218 });
25219 }
25220
25221 if (r.touchData.start) {
25222 r.touchData.start._private.grabbed = false;
25223 }
25224
25225 r.touchData.cxt = true;
25226 r.touchData.cxtDragged = false;
25227 r.data.bgActivePosistion = undefined;
25228 r.redraw();
25229 return;
25230 }
25231 }
25232
25233 if (e.touches[2]) {
25234 // ignore
25235 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25236 if (cy.boxSelectionEnabled()) {
25237 e.preventDefault();
25238 }
25239 } else if (e.touches[1]) ; else if (e.touches[0]) {
25240 var nears = r.findNearestElements(now[0], now[1], true, true);
25241 var near = nears[0];
25242
25243 if (near != null) {
25244 near.activate();
25245 r.touchData.start = near;
25246 r.touchData.starts = nears;
25247
25248 if (r.nodeIsGrabbable(near)) {
25249 var draggedEles = r.dragData.touchDragEles = cy.collection();
25250 var selectedNodes = null;
25251 r.redrawHint('eles', true);
25252 r.redrawHint('drag', true);
25253
25254 if (near.selected()) {
25255 // reset drag elements, since near will be added again
25256 selectedNodes = cy.$(function (ele) {
25257 return ele.selected() && r.nodeIsGrabbable(ele);
25258 });
25259 addNodesToDrag(selectedNodes, {
25260 addToList: draggedEles
25261 });
25262 } else {
25263 addNodeToDrag(near, {
25264 addToList: draggedEles
25265 });
25266 }
25267
25268 setGrabTarget(near);
25269
25270 var makeEvent = function makeEvent(type) {
25271 return {
25272 originalEvent: e,
25273 type: type,
25274 position: {
25275 x: now[0],
25276 y: now[1]
25277 }
25278 };
25279 };
25280
25281 near.emit(makeEvent('grabon'));
25282
25283 if (selectedNodes) {
25284 selectedNodes.forEach(function (n) {
25285 n.emit(makeEvent('grab'));
25286 });
25287 } else {
25288 near.emit(makeEvent('grab'));
25289 }
25290 }
25291 }
25292
25293 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25294 x: now[0],
25295 y: now[1]
25296 });
25297
25298 if (near == null) {
25299 r.data.bgActivePosistion = {
25300 x: pos[0],
25301 y: pos[1]
25302 };
25303 r.redrawHint('select', true);
25304 r.redraw();
25305 } // Tap, taphold
25306 // -----
25307
25308
25309 r.touchData.singleTouchMoved = false;
25310 r.touchData.singleTouchStartTime = +new Date();
25311 clearTimeout(r.touchData.tapholdTimeout);
25312 r.touchData.tapholdTimeout = setTimeout(function () {
25313 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25314 && !r.touchData.selecting // box selection shouldn't allow taphold through
25315 ) {
25316 triggerEvents(r.touchData.start, ['taphold'], e, {
25317 x: now[0],
25318 y: now[1]
25319 });
25320 }
25321 }, r.tapholdDuration);
25322 }
25323
25324 if (e.touches.length >= 1) {
25325 var sPos = r.touchData.startPosition = [];
25326
25327 for (var i = 0; i < now.length; i++) {
25328 sPos[i] = earlier[i] = now[i];
25329 }
25330
25331 var touch0 = e.touches[0];
25332 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25333 }
25334 }, false);
25335 var touchmoveHandler;
25336 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25337 // eslint-disable-line no-undef
25338 var capture = r.touchData.capture;
25339
25340 if (!capture && !eventInContainer(e)) {
25341 return;
25342 }
25343
25344 var select = r.selection;
25345 var cy = r.cy;
25346 var now = r.touchData.now;
25347 var earlier = r.touchData.earlier;
25348 var zoom = cy.zoom();
25349
25350 if (e.touches[0]) {
25351 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25352 now[0] = pos[0];
25353 now[1] = pos[1];
25354 }
25355
25356 if (e.touches[1]) {
25357 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25358 now[2] = pos[0];
25359 now[3] = pos[1];
25360 }
25361
25362 if (e.touches[2]) {
25363 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25364 now[4] = pos[0];
25365 now[5] = pos[1];
25366 }
25367
25368 var startGPos = r.touchData.startGPosition;
25369 var isOverThresholdDrag;
25370
25371 if (capture && e.touches[0] && startGPos) {
25372 var disp = [];
25373
25374 for (var j = 0; j < now.length; j++) {
25375 disp[j] = now[j] - earlier[j];
25376 }
25377
25378 var dx = e.touches[0].clientX - startGPos[0];
25379 var dx2 = dx * dx;
25380 var dy = e.touches[0].clientY - startGPos[1];
25381 var dy2 = dy * dy;
25382 var dist2 = dx2 + dy2;
25383 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25384 } // context swipe cancelling
25385
25386
25387 if (capture && r.touchData.cxt) {
25388 e.preventDefault();
25389 var f1x2 = e.touches[0].clientX - offsetLeft,
25390 f1y2 = e.touches[0].clientY - offsetTop;
25391 var f2x2 = e.touches[1].clientX - offsetLeft,
25392 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25393
25394 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25395 var factorSq = distance2Sq / distance1Sq;
25396 var distThreshold = 150;
25397 var distThresholdSq = distThreshold * distThreshold;
25398 var factorThreshold = 1.5;
25399 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25400
25401 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25402 r.touchData.cxt = false;
25403 r.data.bgActivePosistion = undefined;
25404 r.redrawHint('select', true);
25405 var cxtEvt = {
25406 originalEvent: e,
25407 type: 'cxttapend',
25408 position: {
25409 x: now[0],
25410 y: now[1]
25411 }
25412 };
25413
25414 if (r.touchData.start) {
25415 r.touchData.start.unactivate().emit(cxtEvt);
25416 r.touchData.start = null;
25417 } else {
25418 cy.emit(cxtEvt);
25419 }
25420 }
25421 } // context swipe
25422
25423
25424 if (capture && r.touchData.cxt) {
25425 var cxtEvt = {
25426 originalEvent: e,
25427 type: 'cxtdrag',
25428 position: {
25429 x: now[0],
25430 y: now[1]
25431 }
25432 };
25433 r.data.bgActivePosistion = undefined;
25434 r.redrawHint('select', true);
25435
25436 if (r.touchData.start) {
25437 r.touchData.start.emit(cxtEvt);
25438 } else {
25439 cy.emit(cxtEvt);
25440 }
25441
25442 if (r.touchData.start) {
25443 r.touchData.start._private.grabbed = false;
25444 }
25445
25446 r.touchData.cxtDragged = true;
25447 var near = r.findNearestElement(now[0], now[1], true, true);
25448
25449 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25450 if (r.touchData.cxtOver) {
25451 r.touchData.cxtOver.emit({
25452 originalEvent: e,
25453 type: 'cxtdragout',
25454 position: {
25455 x: now[0],
25456 y: now[1]
25457 }
25458 });
25459 }
25460
25461 r.touchData.cxtOver = near;
25462
25463 if (near) {
25464 near.emit({
25465 originalEvent: e,
25466 type: 'cxtdragover',
25467 position: {
25468 x: now[0],
25469 y: now[1]
25470 }
25471 });
25472 }
25473 } // box selection
25474
25475 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25476 e.preventDefault();
25477 r.data.bgActivePosistion = undefined;
25478 this.lastThreeTouch = +new Date();
25479
25480 if (!r.touchData.selecting) {
25481 cy.emit({
25482 originalEvent: e,
25483 type: 'boxstart',
25484 position: {
25485 x: now[0],
25486 y: now[1]
25487 }
25488 });
25489 }
25490
25491 r.touchData.selecting = true;
25492 r.touchData.didSelect = true;
25493 select[4] = 1;
25494
25495 if (!select || select.length === 0 || select[0] === undefined) {
25496 select[0] = (now[0] + now[2] + now[4]) / 3;
25497 select[1] = (now[1] + now[3] + now[5]) / 3;
25498 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25499 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25500 } else {
25501 select[2] = (now[0] + now[2] + now[4]) / 3;
25502 select[3] = (now[1] + now[3] + now[5]) / 3;
25503 }
25504
25505 r.redrawHint('select', true);
25506 r.redraw(); // pinch to zoom
25507 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25508 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25509 // two fingers => pinch to zoom
25510 e.preventDefault();
25511 r.data.bgActivePosistion = undefined;
25512 r.redrawHint('select', true);
25513 var draggedEles = r.dragData.touchDragEles;
25514
25515 if (draggedEles) {
25516 r.redrawHint('drag', true);
25517
25518 for (var i = 0; i < draggedEles.length; i++) {
25519 var de_p = draggedEles[i]._private;
25520 de_p.grabbed = false;
25521 de_p.rscratch.inDragLayer = false;
25522 }
25523 }
25524
25525 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25526
25527 var f1x2 = e.touches[0].clientX - offsetLeft,
25528 f1y2 = e.touches[0].clientY - offsetTop;
25529 var f2x2 = e.touches[1].clientX - offsetLeft,
25530 f2y2 = e.touches[1].clientY - offsetTop;
25531 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25532 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25533
25534 var factor = distance2 / distance1;
25535
25536 if (twoFingersStartInside) {
25537 // delta finger1
25538 var df1x = f1x2 - f1x1;
25539 var df1y = f1y2 - f1y1; // delta finger 2
25540
25541 var df2x = f2x2 - f2x1;
25542 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25543 // i.e. so pinching cancels out and moving together pans
25544
25545 var tx = (df1x + df2x) / 2;
25546 var ty = (df1y + df2y) / 2; // now calculate the zoom
25547
25548 var zoom1 = cy.zoom();
25549 var zoom2 = zoom1 * factor;
25550 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25551
25552 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25553 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25554 var pan2 = {
25555 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25556 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25557 }; // remove dragged eles
25558
25559 if (_start && _start.active()) {
25560 var draggedEles = r.dragData.touchDragEles;
25561 freeDraggedElements(draggedEles);
25562 r.redrawHint('drag', true);
25563 r.redrawHint('eles', true);
25564
25565 _start.unactivate().emit('freeon');
25566
25567 draggedEles.emit('free');
25568
25569 if (r.dragData.didDrag) {
25570 _start.emit('dragfreeon');
25571
25572 draggedEles.emit('dragfree');
25573 }
25574 }
25575
25576 cy.viewport({
25577 zoom: zoom2,
25578 pan: pan2,
25579 cancelOnFailedZoom: true
25580 });
25581 distance1 = distance2;
25582 f1x1 = f1x2;
25583 f1y1 = f1y2;
25584 f2x1 = f2x2;
25585 f2y1 = f2y2;
25586 r.pinching = true;
25587 } // Re-project
25588
25589
25590 if (e.touches[0]) {
25591 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25592 now[0] = pos[0];
25593 now[1] = pos[1];
25594 }
25595
25596 if (e.touches[1]) {
25597 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25598 now[2] = pos[0];
25599 now[3] = pos[1];
25600 }
25601
25602 if (e.touches[2]) {
25603 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25604 now[4] = pos[0];
25605 now[5] = pos[1];
25606 }
25607 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25608 ) {
25609 var start = r.touchData.start;
25610 var last = r.touchData.last;
25611 var near;
25612
25613 if (!r.hoverData.draggingEles && !r.swipePanning) {
25614 near = r.findNearestElement(now[0], now[1], true, true);
25615 }
25616
25617 if (capture && start != null) {
25618 e.preventDefault();
25619 } // dragging nodes
25620
25621
25622 if (capture && start != null && r.nodeIsDraggable(start)) {
25623 if (isOverThresholdDrag) {
25624 // then dragging can happen
25625 var draggedEles = r.dragData.touchDragEles;
25626 var justStartedDrag = !r.dragData.didDrag;
25627
25628 if (justStartedDrag) {
25629 addNodesToDrag(draggedEles, {
25630 inDragLayer: true
25631 });
25632 }
25633
25634 r.dragData.didDrag = true;
25635 var totalShift = {
25636 x: 0,
25637 y: 0
25638 };
25639
25640 if (number(disp[0]) && number(disp[1])) {
25641 totalShift.x += disp[0];
25642 totalShift.y += disp[1];
25643
25644 if (justStartedDrag) {
25645 r.redrawHint('eles', true);
25646 var dragDelta = r.touchData.dragDelta;
25647
25648 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25649 totalShift.x += dragDelta[0];
25650 totalShift.y += dragDelta[1];
25651 }
25652 }
25653 }
25654
25655 r.hoverData.draggingEles = true;
25656 draggedEles.silentShift(totalShift).emit('position drag');
25657 r.redrawHint('drag', true);
25658
25659 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25660 r.redrawHint('eles', true);
25661 }
25662
25663 r.redraw();
25664 } else {
25665 // otherise keep track of drag delta for later
25666 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25667
25668 if (dragDelta.length === 0) {
25669 dragDelta.push(disp[0]);
25670 dragDelta.push(disp[1]);
25671 } else {
25672 dragDelta[0] += disp[0];
25673 dragDelta[1] += disp[1];
25674 }
25675 }
25676 } // touchmove
25677
25678
25679 {
25680 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25681 x: now[0],
25682 y: now[1]
25683 });
25684
25685 if ((!start || !start.grabbed()) && near != last) {
25686 if (last) {
25687 last.emit({
25688 originalEvent: e,
25689 type: 'tapdragout',
25690 position: {
25691 x: now[0],
25692 y: now[1]
25693 }
25694 });
25695 }
25696
25697 if (near) {
25698 near.emit({
25699 originalEvent: e,
25700 type: 'tapdragover',
25701 position: {
25702 x: now[0],
25703 y: now[1]
25704 }
25705 });
25706 }
25707 }
25708
25709 r.touchData.last = near;
25710 } // check to cancel taphold
25711
25712 if (capture) {
25713 for (var i = 0; i < now.length; i++) {
25714 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25715 r.touchData.singleTouchMoved = true;
25716 }
25717 }
25718 } // panning
25719
25720
25721 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25722 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25723
25724 if (allowPassthrough) {
25725 e.preventDefault();
25726
25727 if (!r.data.bgActivePosistion) {
25728 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25729 }
25730
25731 if (r.swipePanning) {
25732 cy.panBy({
25733 x: disp[0] * zoom,
25734 y: disp[1] * zoom
25735 });
25736 } else if (isOverThresholdDrag) {
25737 r.swipePanning = true;
25738 cy.panBy({
25739 x: dx * zoom,
25740 y: dy * zoom
25741 });
25742
25743 if (start) {
25744 start.unactivate();
25745 r.redrawHint('select', true);
25746 r.touchData.start = null;
25747 }
25748 }
25749 } // Re-project
25750
25751
25752 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25753 now[0] = pos[0];
25754 now[1] = pos[1];
25755 }
25756 }
25757
25758 for (var j = 0; j < now.length; j++) {
25759 earlier[j] = now[j];
25760 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25761
25762
25763 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
25764 r.data.bgActivePosistion = undefined;
25765 r.redrawHint('select', true);
25766 r.redraw();
25767 }
25768 }, false);
25769 var touchcancelHandler;
25770 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
25771 // eslint-disable-line no-unused-vars
25772 var start = r.touchData.start;
25773 r.touchData.capture = false;
25774
25775 if (start) {
25776 start.unactivate();
25777 }
25778 });
25779 var touchendHandler;
25780 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
25781 // eslint-disable-line no-unused-vars
25782 var start = r.touchData.start;
25783 var capture = r.touchData.capture;
25784
25785 if (capture) {
25786 if (e.touches.length === 0) {
25787 r.touchData.capture = false;
25788 }
25789
25790 e.preventDefault();
25791 } else {
25792 return;
25793 }
25794
25795 var select = r.selection;
25796 r.swipePanning = false;
25797 r.hoverData.draggingEles = false;
25798 var cy = r.cy;
25799 var zoom = cy.zoom();
25800 var now = r.touchData.now;
25801 var earlier = r.touchData.earlier;
25802
25803 if (e.touches[0]) {
25804 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25805 now[0] = pos[0];
25806 now[1] = pos[1];
25807 }
25808
25809 if (e.touches[1]) {
25810 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25811 now[2] = pos[0];
25812 now[3] = pos[1];
25813 }
25814
25815 if (e.touches[2]) {
25816 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25817 now[4] = pos[0];
25818 now[5] = pos[1];
25819 }
25820
25821 if (start) {
25822 start.unactivate();
25823 }
25824
25825 var ctxTapend;
25826
25827 if (r.touchData.cxt) {
25828 ctxTapend = {
25829 originalEvent: e,
25830 type: 'cxttapend',
25831 position: {
25832 x: now[0],
25833 y: now[1]
25834 }
25835 };
25836
25837 if (start) {
25838 start.emit(ctxTapend);
25839 } else {
25840 cy.emit(ctxTapend);
25841 }
25842
25843 if (!r.touchData.cxtDragged) {
25844 var ctxTap = {
25845 originalEvent: e,
25846 type: 'cxttap',
25847 position: {
25848 x: now[0],
25849 y: now[1]
25850 }
25851 };
25852
25853 if (start) {
25854 start.emit(ctxTap);
25855 } else {
25856 cy.emit(ctxTap);
25857 }
25858 }
25859
25860 if (r.touchData.start) {
25861 r.touchData.start._private.grabbed = false;
25862 }
25863
25864 r.touchData.cxt = false;
25865 r.touchData.start = null;
25866 r.redraw();
25867 return;
25868 } // no more box selection if we don't have three fingers
25869
25870
25871 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
25872 r.touchData.selecting = false;
25873 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25874 select[0] = undefined;
25875 select[1] = undefined;
25876 select[2] = undefined;
25877 select[3] = undefined;
25878 select[4] = 0;
25879 r.redrawHint('select', true);
25880 cy.emit({
25881 type: 'boxend',
25882 originalEvent: e,
25883 position: {
25884 x: now[0],
25885 y: now[1]
25886 }
25887 });
25888
25889 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25890 return ele.selectable() && !ele.selected();
25891 };
25892
25893 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25894
25895 if (box.nonempty()) {
25896 r.redrawHint('eles', true);
25897 }
25898
25899 r.redraw();
25900 }
25901
25902 if (start != null) {
25903 start.unactivate();
25904 }
25905
25906 if (e.touches[2]) {
25907 r.data.bgActivePosistion = undefined;
25908 r.redrawHint('select', true);
25909 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
25910 r.data.bgActivePosistion = undefined;
25911 r.redrawHint('select', true);
25912 var draggedEles = r.dragData.touchDragEles;
25913
25914 if (start != null) {
25915 var startWasGrabbed = start._private.grabbed;
25916 freeDraggedElements(draggedEles);
25917 r.redrawHint('drag', true);
25918 r.redrawHint('eles', true);
25919
25920 if (startWasGrabbed) {
25921 start.emit('freeon');
25922 draggedEles.emit('free');
25923
25924 if (r.dragData.didDrag) {
25925 start.emit('dragfreeon');
25926 draggedEles.emit('dragfree');
25927 }
25928 }
25929
25930 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
25931 x: now[0],
25932 y: now[1]
25933 });
25934 start.unactivate();
25935 r.touchData.start = null;
25936 } else {
25937 var near = r.findNearestElement(now[0], now[1], true, true);
25938 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
25939 x: now[0],
25940 y: now[1]
25941 });
25942 }
25943
25944 var dx = r.touchData.startPosition[0] - now[0];
25945 var dx2 = dx * dx;
25946 var dy = r.touchData.startPosition[1] - now[1];
25947 var dy2 = dy * dy;
25948 var dist2 = dx2 + dy2;
25949 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
25950
25951 if (!r.touchData.singleTouchMoved) {
25952 if (!start) {
25953 cy.$(':selected').unselect(['tapunselect']);
25954 }
25955
25956 triggerEvents(start, ['tap', 'vclick'], e, {
25957 x: now[0],
25958 y: now[1]
25959 });
25960 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
25961
25962
25963 if (start != null && !r.dragData.didDrag // didn't drag nodes around
25964 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
25965 ) {
25966 if (cy.selectionType() === 'single') {
25967 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
25968 start.select(['tapselect']);
25969 } else {
25970 if (start.selected()) {
25971 start.unselect(['tapunselect']);
25972 } else {
25973 start.select(['tapselect']);
25974 }
25975 }
25976
25977 r.redrawHint('eles', true);
25978 }
25979
25980 r.touchData.singleTouchMoved = true;
25981 }
25982
25983 for (var j = 0; j < now.length; j++) {
25984 earlier[j] = now[j];
25985 }
25986
25987 r.dragData.didDrag = false; // reset for next touchstart
25988
25989 if (e.touches.length === 0) {
25990 r.touchData.dragDelta = [];
25991 r.touchData.startPosition = null;
25992 r.touchData.startGPosition = null;
25993 r.touchData.didSelect = false;
25994 }
25995
25996 if (e.touches.length < 2) {
25997 if (e.touches.length === 1) {
25998 // the old start global pos'n may not be the same finger that remains
25999 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26000 }
26001
26002 r.pinching = false;
26003 r.redrawHint('eles', true);
26004 r.redraw();
26005 } //r.redraw();
26006
26007 }, false); // fallback compatibility layer for ms pointer events
26008
26009 if (typeof TouchEvent === 'undefined') {
26010 var pointers = [];
26011
26012 var makeTouch = function makeTouch(e) {
26013 return {
26014 clientX: e.clientX,
26015 clientY: e.clientY,
26016 force: 1,
26017 identifier: e.pointerId,
26018 pageX: e.pageX,
26019 pageY: e.pageY,
26020 radiusX: e.width / 2,
26021 radiusY: e.height / 2,
26022 screenX: e.screenX,
26023 screenY: e.screenY,
26024 target: e.target
26025 };
26026 };
26027
26028 var makePointer = function makePointer(e) {
26029 return {
26030 event: e,
26031 touch: makeTouch(e)
26032 };
26033 };
26034
26035 var addPointer = function addPointer(e) {
26036 pointers.push(makePointer(e));
26037 };
26038
26039 var removePointer = function removePointer(e) {
26040 for (var i = 0; i < pointers.length; i++) {
26041 var p = pointers[i];
26042
26043 if (p.event.pointerId === e.pointerId) {
26044 pointers.splice(i, 1);
26045 return;
26046 }
26047 }
26048 };
26049
26050 var updatePointer = function updatePointer(e) {
26051 var p = pointers.filter(function (p) {
26052 return p.event.pointerId === e.pointerId;
26053 })[0];
26054 p.event = e;
26055 p.touch = makeTouch(e);
26056 };
26057
26058 var addTouchesToEvent = function addTouchesToEvent(e) {
26059 e.touches = pointers.map(function (p) {
26060 return p.touch;
26061 });
26062 };
26063
26064 var pointerIsMouse = function pointerIsMouse(e) {
26065 return e.pointerType === 'mouse' || e.pointerType === 4;
26066 };
26067
26068 r.registerBinding(r.container, 'pointerdown', function (e) {
26069 if (pointerIsMouse(e)) {
26070 return;
26071 } // mouse already handled
26072
26073
26074 e.preventDefault();
26075 addPointer(e);
26076 addTouchesToEvent(e);
26077 touchstartHandler(e);
26078 });
26079 r.registerBinding(r.container, 'pointerup', function (e) {
26080 if (pointerIsMouse(e)) {
26081 return;
26082 } // mouse already handled
26083
26084
26085 removePointer(e);
26086 addTouchesToEvent(e);
26087 touchendHandler(e);
26088 });
26089 r.registerBinding(r.container, 'pointercancel', function (e) {
26090 if (pointerIsMouse(e)) {
26091 return;
26092 } // mouse already handled
26093
26094
26095 removePointer(e);
26096 addTouchesToEvent(e);
26097 touchcancelHandler(e);
26098 });
26099 r.registerBinding(r.container, 'pointermove', function (e) {
26100 if (pointerIsMouse(e)) {
26101 return;
26102 } // mouse already handled
26103
26104
26105 e.preventDefault();
26106 updatePointer(e);
26107 addTouchesToEvent(e);
26108 touchmoveHandler(e);
26109 });
26110 }
26111};
26112
26113var BRp$d = {};
26114
26115BRp$d.generatePolygon = function (name, points) {
26116 return this.nodeShapes[name] = {
26117 renderer: this,
26118 name: name,
26119 points: points,
26120 draw: function draw(context, centerX, centerY, width, height) {
26121 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26122 },
26123 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26124 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26125 },
26126 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26127 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26128 }
26129 };
26130};
26131
26132BRp$d.generateEllipse = function () {
26133 return this.nodeShapes['ellipse'] = {
26134 renderer: this,
26135 name: 'ellipse',
26136 draw: function draw(context, centerX, centerY, width, height) {
26137 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26138 },
26139 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26140 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26141 },
26142 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26143 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26144 }
26145 };
26146};
26147
26148BRp$d.generateRoundPolygon = function (name, points) {
26149 // Pre-compute control points
26150 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26151 // the unit vectors.
26152 // For simplicity the layout will be:
26153 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26154 var allPoints = new Array(points.length * 2);
26155
26156 for (var i = 0; i < points.length / 2; i++) {
26157 var sourceIndex = i * 2;
26158 var destIndex = void 0;
26159
26160 if (i < points.length / 2 - 1) {
26161 destIndex = (i + 1) * 2;
26162 } else {
26163 destIndex = 0;
26164 }
26165
26166 allPoints[i * 4] = points[sourceIndex];
26167 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26168 var xDest = points[destIndex] - points[sourceIndex];
26169 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26170 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26171 allPoints[i * 4 + 2] = xDest / norm;
26172 allPoints[i * 4 + 3] = yDest / norm;
26173 }
26174
26175 return this.nodeShapes[name] = {
26176 renderer: this,
26177 name: name,
26178 points: allPoints,
26179 draw: function draw(context, centerX, centerY, width, height) {
26180 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26181 },
26182 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26183 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26184 },
26185 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26186 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26187 }
26188 };
26189};
26190
26191BRp$d.generateRoundRectangle = function () {
26192 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26193 renderer: this,
26194 name: 'round-rectangle',
26195 points: generateUnitNgonPointsFitToSquare(4, 0),
26196 draw: function draw(context, centerX, centerY, width, height) {
26197 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26198 },
26199 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26200 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26201 },
26202 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26203 var cornerRadius = getRoundRectangleRadius(width, height);
26204 var diam = cornerRadius * 2; // Check hBox
26205
26206 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26207 return true;
26208 } // Check vBox
26209
26210
26211 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26212 return true;
26213 } // Check top left quarter circle
26214
26215
26216 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26217 return true;
26218 } // Check top right quarter circle
26219
26220
26221 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26222 return true;
26223 } // Check bottom right quarter circle
26224
26225
26226 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26227 return true;
26228 } // Check bottom left quarter circle
26229
26230
26231 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26232 return true;
26233 }
26234
26235 return false;
26236 }
26237 };
26238};
26239
26240BRp$d.generateCutRectangle = function () {
26241 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26242 renderer: this,
26243 name: 'cut-rectangle',
26244 cornerLength: getCutRectangleCornerLength(),
26245 points: generateUnitNgonPointsFitToSquare(4, 0),
26246 draw: function draw(context, centerX, centerY, width, height) {
26247 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26248 },
26249 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
26250 var cl = this.cornerLength;
26251 var hh = height / 2;
26252 var hw = width / 2;
26253 var xBegin = centerX - hw;
26254 var xEnd = centerX + hw;
26255 var yBegin = centerY - hh;
26256 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26257
26258 return {
26259 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26260 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26261 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26262 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26263 };
26264 },
26265 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26266 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26267 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26268 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26269 },
26270 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26271 // Check hBox
26272 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
26273 return true;
26274 } // Check vBox
26275
26276
26277 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
26278 return true;
26279 }
26280
26281 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26282 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26283 }
26284 };
26285};
26286
26287BRp$d.generateBarrel = function () {
26288 return this.nodeShapes['barrel'] = {
26289 renderer: this,
26290 name: 'barrel',
26291 points: generateUnitNgonPointsFitToSquare(4, 0),
26292 draw: function draw(context, centerX, centerY, width, height) {
26293 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26294 },
26295 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26296 // use two fixed t values for the bezier curve approximation
26297 var t0 = 0.15;
26298 var t1 = 0.5;
26299 var t2 = 0.85;
26300 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26301
26302 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26303 // approximate curve pts based on the two t values
26304 var m0 = qbezierPtAt({
26305 x: pts[0],
26306 y: pts[1]
26307 }, {
26308 x: pts[2],
26309 y: pts[3]
26310 }, {
26311 x: pts[4],
26312 y: pts[5]
26313 }, t0);
26314 var m1 = qbezierPtAt({
26315 x: pts[0],
26316 y: pts[1]
26317 }, {
26318 x: pts[2],
26319 y: pts[3]
26320 }, {
26321 x: pts[4],
26322 y: pts[5]
26323 }, t1);
26324 var m2 = qbezierPtAt({
26325 x: pts[0],
26326 y: pts[1]
26327 }, {
26328 x: pts[2],
26329 y: pts[3]
26330 }, {
26331 x: pts[4],
26332 y: pts[5]
26333 }, t2);
26334 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26335 };
26336
26337 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26338 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26339 },
26340 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26341 var hh = height / 2;
26342 var hw = width / 2;
26343 var xBegin = centerX - hw;
26344 var xEnd = centerX + hw;
26345 var yBegin = centerY - hh;
26346 var yEnd = centerY + hh;
26347 var curveConstants = getBarrelCurveConstants(width, height);
26348 var hOffset = curveConstants.heightOffset;
26349 var wOffset = curveConstants.widthOffset;
26350 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26351
26352 var pts = {
26353 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26354 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26355 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26356 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26357 };
26358 pts.topLeft.isTop = true;
26359 pts.topRight.isTop = true;
26360 pts.bottomLeft.isBottom = true;
26361 pts.bottomRight.isBottom = true;
26362 return pts;
26363 },
26364 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26365 var curveConstants = getBarrelCurveConstants(width, height);
26366 var hOffset = curveConstants.heightOffset;
26367 var wOffset = curveConstants.widthOffset; // Check hBox
26368
26369 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26370 return true;
26371 } // Check vBox
26372
26373
26374 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26375 return true;
26376 }
26377
26378 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26379
26380 var getCurveT = function getCurveT(x, y, curvePts) {
26381 var x0 = curvePts[4];
26382 var x1 = curvePts[2];
26383 var x2 = curvePts[0];
26384 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26385
26386 var y2 = curvePts[1];
26387 var xMin = Math.min(x0, x2);
26388 var xMax = Math.max(x0, x2);
26389 var yMin = Math.min(y0, y2);
26390 var yMax = Math.max(y0, y2);
26391
26392 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26393 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26394 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26395 var validRoots = roots.filter(function (r) {
26396 return 0 <= r && r <= 1;
26397 });
26398
26399 if (validRoots.length > 0) {
26400 return validRoots[0];
26401 }
26402 }
26403
26404 return null;
26405 };
26406
26407 var curveRegions = Object.keys(barrelCurvePts);
26408
26409 for (var i = 0; i < curveRegions.length; i++) {
26410 var corner = curveRegions[i];
26411 var cornerPts = barrelCurvePts[corner];
26412 var t = getCurveT(x, y, cornerPts);
26413
26414 if (t == null) {
26415 continue;
26416 }
26417
26418 var y0 = cornerPts[5];
26419 var y1 = cornerPts[3];
26420 var y2 = cornerPts[1];
26421 var bezY = qbezierAt(y0, y1, y2, t);
26422
26423 if (cornerPts.isTop && bezY <= y) {
26424 return true;
26425 }
26426
26427 if (cornerPts.isBottom && y <= bezY) {
26428 return true;
26429 }
26430 }
26431
26432 return false;
26433 }
26434 };
26435};
26436
26437BRp$d.generateBottomRoundrectangle = function () {
26438 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26439 renderer: this,
26440 name: 'bottom-round-rectangle',
26441 points: generateUnitNgonPointsFitToSquare(4, 0),
26442 draw: function draw(context, centerX, centerY, width, height) {
26443 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26444 },
26445 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26446 var topStartX = nodeX - (width / 2 + padding);
26447 var topStartY = nodeY - (height / 2 + padding);
26448 var topEndY = topStartY;
26449 var topEndX = nodeX + (width / 2 + padding);
26450 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26451
26452 if (topIntersections.length > 0) {
26453 return topIntersections;
26454 }
26455
26456 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26457 },
26458 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26459 var cornerRadius = getRoundRectangleRadius(width, height);
26460 var diam = 2 * cornerRadius; // Check hBox
26461
26462 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26463 return true;
26464 } // Check vBox
26465
26466
26467 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26468 return true;
26469 } // check non-rounded top side
26470
26471
26472 var outerWidth = width / 2 + 2 * padding;
26473 var outerHeight = height / 2 + 2 * padding;
26474 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26475
26476 if (pointInsidePolygonPoints(x, y, points)) {
26477 return true;
26478 } // Check bottom right quarter circle
26479
26480
26481 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26482 return true;
26483 } // Check bottom left quarter circle
26484
26485
26486 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26487 return true;
26488 }
26489
26490 return false;
26491 }
26492 };
26493};
26494
26495BRp$d.registerNodeShapes = function () {
26496 var nodeShapes = this.nodeShapes = {};
26497 var renderer = this;
26498 this.generateEllipse();
26499 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26500 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26501 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26502 nodeShapes['square'] = nodeShapes['rectangle'];
26503 this.generateRoundRectangle();
26504 this.generateCutRectangle();
26505 this.generateBarrel();
26506 this.generateBottomRoundrectangle();
26507 {
26508 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26509 this.generatePolygon('diamond', diamondPoints);
26510 this.generateRoundPolygon('round-diamond', diamondPoints);
26511 }
26512 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26513 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26514 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26515 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26516 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26517 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26518 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26519 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26520 var star5Points = new Array(20);
26521 {
26522 var outerPoints = generateUnitNgonPoints(5, 0);
26523 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26524
26525 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26526 innerRadius *= 1.57;
26527
26528 for (var i = 0; i < innerPoints.length / 2; i++) {
26529 innerPoints[i * 2] *= innerRadius;
26530 innerPoints[i * 2 + 1] *= innerRadius;
26531 }
26532
26533 for (var i = 0; i < 20 / 4; i++) {
26534 star5Points[i * 4] = outerPoints[i * 2];
26535 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26536 star5Points[i * 4 + 2] = innerPoints[i * 2];
26537 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26538 }
26539 }
26540 star5Points = fitPolygonToSquare(star5Points);
26541 this.generatePolygon('star', star5Points);
26542 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26543 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26544 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]);
26545 {
26546 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26547 this.generatePolygon('tag', tagPoints);
26548 this.generateRoundPolygon('round-tag', tagPoints);
26549 }
26550
26551 nodeShapes.makePolygon = function (points) {
26552 // use caching on user-specified polygons so they are as fast as native shapes
26553 var key = points.join('$');
26554 var name = 'polygon-' + key;
26555 var shape;
26556
26557 if (shape = this[name]) {
26558 // got cached shape
26559 return shape;
26560 } // create and cache new shape
26561
26562
26563 return renderer.generatePolygon(name, points);
26564 };
26565};
26566
26567var BRp$e = {};
26568
26569BRp$e.timeToRender = function () {
26570 return this.redrawTotalTime / this.redrawCount;
26571};
26572
26573BRp$e.redraw = function (options) {
26574 options = options || staticEmptyObject();
26575 var r = this;
26576
26577 if (r.averageRedrawTime === undefined) {
26578 r.averageRedrawTime = 0;
26579 }
26580
26581 if (r.lastRedrawTime === undefined) {
26582 r.lastRedrawTime = 0;
26583 }
26584
26585 if (r.lastDrawTime === undefined) {
26586 r.lastDrawTime = 0;
26587 }
26588
26589 r.requestedFrame = true;
26590 r.renderOptions = options;
26591};
26592
26593BRp$e.beforeRender = function (fn, priority) {
26594 // the renderer can't add tick callbacks when destroyed
26595 if (this.destroyed) {
26596 return;
26597 }
26598
26599 if (priority == null) {
26600 error('Priority is not optional for beforeRender');
26601 }
26602
26603 var cbs = this.beforeRenderCallbacks;
26604 cbs.push({
26605 fn: fn,
26606 priority: priority
26607 }); // higher priority callbacks executed first
26608
26609 cbs.sort(function (a, b) {
26610 return b.priority - a.priority;
26611 });
26612};
26613
26614var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26615 var cbs = r.beforeRenderCallbacks;
26616
26617 for (var i = 0; i < cbs.length; i++) {
26618 cbs[i].fn(willDraw, startTime);
26619 }
26620};
26621
26622BRp$e.startRenderLoop = function () {
26623 var r = this;
26624 var cy = r.cy;
26625
26626 if (r.renderLoopStarted) {
26627 return;
26628 } else {
26629 r.renderLoopStarted = true;
26630 }
26631
26632 var renderFn = function renderFn(requestTime) {
26633 if (r.destroyed) {
26634 return;
26635 }
26636
26637 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26638 beforeRenderCallbacks(r, true, requestTime);
26639 var startTime = performanceNow();
26640 r.render(r.renderOptions);
26641 var endTime = r.lastDrawTime = performanceNow();
26642
26643 if (r.averageRedrawTime === undefined) {
26644 r.averageRedrawTime = endTime - startTime;
26645 }
26646
26647 if (r.redrawCount === undefined) {
26648 r.redrawCount = 0;
26649 }
26650
26651 r.redrawCount++;
26652
26653 if (r.redrawTotalTime === undefined) {
26654 r.redrawTotalTime = 0;
26655 }
26656
26657 var duration = endTime - startTime;
26658 r.redrawTotalTime += duration;
26659 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26660
26661 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26662 r.requestedFrame = false;
26663 } else {
26664 beforeRenderCallbacks(r, false, requestTime);
26665 }
26666
26667 r.skipFrame = false;
26668 requestAnimationFrame(renderFn);
26669 };
26670
26671 requestAnimationFrame(renderFn);
26672};
26673
26674var BaseRenderer = function BaseRenderer(options) {
26675 this.init(options);
26676};
26677
26678var BR = BaseRenderer;
26679var BRp$f = BR.prototype;
26680BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26681
26682BRp$f.init = function (options) {
26683 var r = this;
26684 r.options = options;
26685 r.cy = options.cy;
26686 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26687
26688 if (window$1) {
26689 var document = window$1.document;
26690 var head = document.head;
26691 var stylesheetId = '__________cytoscape_stylesheet';
26692 var className = '__________cytoscape_container';
26693 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26694
26695 if (ctr.className.indexOf(className) < 0) {
26696 ctr.className = (ctr.className || '') + ' ' + className;
26697 }
26698
26699 if (!stylesheetAlreadyExists) {
26700 var stylesheet = document.createElement('style');
26701 stylesheet.id = stylesheetId;
26702 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26703 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26704 }
26705
26706 var computedStyle = window$1.getComputedStyle(ctr);
26707 var position = computedStyle.getPropertyValue('position');
26708
26709 if (position === 'static') {
26710 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26711 }
26712 }
26713
26714 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26715
26716 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
26717
26718 r.hoverData = {
26719 down: null,
26720 last: null,
26721 downTime: null,
26722 triggerMode: null,
26723 dragging: false,
26724 initialPan: [null, null],
26725 capture: false
26726 };
26727 r.dragData = {
26728 possibleDragElements: []
26729 };
26730 r.touchData = {
26731 start: null,
26732 capture: false,
26733 // These 3 fields related to tap, taphold events
26734 startPosition: [null, null, null, null, null, null],
26735 singleTouchStartTime: null,
26736 singleTouchMoved: true,
26737 now: [null, null, null, null, null, null],
26738 earlier: [null, null, null, null, null, null]
26739 };
26740 r.redraws = 0;
26741 r.showFps = options.showFps;
26742 r.debug = options.debug;
26743 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
26744 r.textureOnViewport = options.textureOnViewport;
26745 r.wheelSensitivity = options.wheelSensitivity;
26746 r.motionBlurEnabled = options.motionBlur; // on by default
26747
26748 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
26749 r.motionBlur = options.motionBlur; // for initial kick off
26750
26751 r.motionBlurOpacity = options.motionBlurOpacity;
26752 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
26753 r.motionBlurPxRatio = 1;
26754 r.mbPxRBlurry = 1; //0.8;
26755
26756 r.minMbLowQualFrames = 4;
26757 r.fullQualityMb = false;
26758 r.clearedForMotionBlur = [];
26759 r.desktopTapThreshold = options.desktopTapThreshold;
26760 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
26761 r.touchTapThreshold = options.touchTapThreshold;
26762 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
26763 r.tapholdDuration = 500;
26764 r.bindings = [];
26765 r.beforeRenderCallbacks = [];
26766 r.beforeRenderPriorities = {
26767 // higher priority execs before lower one
26768 animations: 400,
26769 eleCalcs: 300,
26770 eleTxrDeq: 200,
26771 lyrTxrDeq: 150,
26772 lyrTxrSkip: 100
26773 };
26774 r.registerNodeShapes();
26775 r.registerArrowShapes();
26776 r.registerCalculationListeners();
26777};
26778
26779BRp$f.notify = function (eventName, eles) {
26780 var r = this;
26781 var cy = r.cy; // the renderer can't be notified after it's destroyed
26782
26783 if (this.destroyed) {
26784 return;
26785 }
26786
26787 if (eventName === 'init') {
26788 r.load();
26789 return;
26790 }
26791
26792 if (eventName === 'destroy') {
26793 r.destroy();
26794 return;
26795 }
26796
26797 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
26798 r.invalidateCachedZSortedEles();
26799 }
26800
26801 if (eventName === 'viewport') {
26802 r.redrawHint('select', true);
26803 }
26804
26805 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
26806 r.invalidateContainerClientCoordsCache();
26807 r.matchCanvasSize(r.container);
26808 }
26809
26810 r.redrawHint('eles', true);
26811 r.redrawHint('drag', true);
26812 this.startRenderLoop();
26813 this.redraw();
26814};
26815
26816BRp$f.destroy = function () {
26817 var r = this;
26818 r.destroyed = true;
26819 r.cy.stopAnimationLoop();
26820
26821 for (var i = 0; i < r.bindings.length; i++) {
26822 var binding = r.bindings[i];
26823 var b = binding;
26824 var tgt = b.target;
26825 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
26826 }
26827
26828 r.bindings = [];
26829 r.beforeRenderCallbacks = [];
26830 r.onUpdateEleCalcsFns = [];
26831
26832 if (r.removeObserver) {
26833 r.removeObserver.disconnect();
26834 }
26835
26836 if (r.styleObserver) {
26837 r.styleObserver.disconnect();
26838 }
26839
26840 if (r.resizeObserver) {
26841 r.resizeObserver.disconnect();
26842 }
26843
26844 if (r.labelCalcDiv) {
26845 try {
26846 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
26847 } catch (e) {// ie10 issue #1014
26848 }
26849 }
26850};
26851
26852BRp$f.isHeadless = function () {
26853 return false;
26854};
26855
26856[BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
26857 extend(BRp$f, props);
26858});
26859
26860var fullFpsTime = 1000 / 60; // assume 60 frames per second
26861
26862var defs = {
26863 setupDequeueing: function setupDequeueing(opts) {
26864 return function setupDequeueingImpl() {
26865 var self = this;
26866 var r = this.renderer;
26867
26868 if (self.dequeueingSetup) {
26869 return;
26870 } else {
26871 self.dequeueingSetup = true;
26872 }
26873
26874 var queueRedraw = util(function () {
26875 r.redrawHint('eles', true);
26876 r.redrawHint('drag', true);
26877 r.redraw();
26878 }, opts.deqRedrawThreshold);
26879
26880 var dequeue = function dequeue(willDraw, frameStartTime) {
26881 var startTime = performanceNow();
26882 var avgRenderTime = r.averageRedrawTime;
26883 var renderTime = r.lastRedrawTime;
26884 var deqd = [];
26885 var extent = r.cy.extent();
26886 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
26887 // queue won't automatically be flushed before dequeueing starts
26888
26889 if (!willDraw) {
26890 r.flushRenderedStyleQueue();
26891 }
26892
26893 while (true) {
26894 // eslint-disable-line no-constant-condition
26895 var now = performanceNow();
26896 var duration = now - startTime;
26897 var frameDuration = now - frameStartTime;
26898
26899 if (renderTime < fullFpsTime) {
26900 // if we're rendering faster than the ideal fps, then do dequeueing
26901 // during all of the remaining frame time
26902 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
26903
26904 if (frameDuration >= opts.deqFastCost * timeAvailable) {
26905 break;
26906 }
26907 } else {
26908 if (willDraw) {
26909 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
26910 break;
26911 }
26912 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
26913 break;
26914 }
26915 }
26916
26917 var thisDeqd = opts.deq(self, pixelRatio, extent);
26918
26919 if (thisDeqd.length > 0) {
26920 for (var i = 0; i < thisDeqd.length; i++) {
26921 deqd.push(thisDeqd[i]);
26922 }
26923 } else {
26924 break;
26925 }
26926 } // callbacks on dequeue
26927
26928
26929 if (deqd.length > 0) {
26930 opts.onDeqd(self, deqd);
26931
26932 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
26933 queueRedraw();
26934 }
26935 }
26936 };
26937
26938 var priority = opts.priority || noop;
26939 r.beforeRender(dequeue, priority(self));
26940 };
26941 }
26942};
26943
26944// Uses keys so elements may share the same cache.
26945
26946var ElementTextureCacheLookup =
26947/*#__PURE__*/
26948function () {
26949 function ElementTextureCacheLookup(getKey) {
26950 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
26951
26952 _classCallCheck(this, ElementTextureCacheLookup);
26953
26954 this.idsByKey = new Map$1();
26955 this.keyForId = new Map$1();
26956 this.cachesByLvl = new Map$1();
26957 this.lvls = [];
26958 this.getKey = getKey;
26959 this.doesEleInvalidateKey = doesEleInvalidateKey;
26960 }
26961
26962 _createClass(ElementTextureCacheLookup, [{
26963 key: "getIdsFor",
26964 value: function getIdsFor(key) {
26965 if (key == null) {
26966 error("Can not get id list for null key");
26967 }
26968
26969 var idsByKey = this.idsByKey;
26970 var ids = this.idsByKey.get(key);
26971
26972 if (!ids) {
26973 ids = new Set$1();
26974 idsByKey.set(key, ids);
26975 }
26976
26977 return ids;
26978 }
26979 }, {
26980 key: "addIdForKey",
26981 value: function addIdForKey(key, id) {
26982 if (key != null) {
26983 this.getIdsFor(key).add(id);
26984 }
26985 }
26986 }, {
26987 key: "deleteIdForKey",
26988 value: function deleteIdForKey(key, id) {
26989 if (key != null) {
26990 this.getIdsFor(key)["delete"](id);
26991 }
26992 }
26993 }, {
26994 key: "getNumberOfIdsForKey",
26995 value: function getNumberOfIdsForKey(key) {
26996 if (key == null) {
26997 return 0;
26998 } else {
26999 return this.getIdsFor(key).size;
27000 }
27001 }
27002 }, {
27003 key: "updateKeyMappingFor",
27004 value: function updateKeyMappingFor(ele) {
27005 var id = ele.id();
27006 var prevKey = this.keyForId.get(id);
27007 var currKey = this.getKey(ele);
27008 this.deleteIdForKey(prevKey, id);
27009 this.addIdForKey(currKey, id);
27010 this.keyForId.set(id, currKey);
27011 }
27012 }, {
27013 key: "deleteKeyMappingFor",
27014 value: function deleteKeyMappingFor(ele) {
27015 var id = ele.id();
27016 var prevKey = this.keyForId.get(id);
27017 this.deleteIdForKey(prevKey, id);
27018 this.keyForId["delete"](id);
27019 }
27020 }, {
27021 key: "keyHasChangedFor",
27022 value: function keyHasChangedFor(ele) {
27023 var id = ele.id();
27024 var prevKey = this.keyForId.get(id);
27025 var newKey = this.getKey(ele);
27026 return prevKey !== newKey;
27027 }
27028 }, {
27029 key: "isInvalid",
27030 value: function isInvalid(ele) {
27031 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27032 }
27033 }, {
27034 key: "getCachesAt",
27035 value: function getCachesAt(lvl) {
27036 var cachesByLvl = this.cachesByLvl,
27037 lvls = this.lvls;
27038 var caches = cachesByLvl.get(lvl);
27039
27040 if (!caches) {
27041 caches = new Map$1();
27042 cachesByLvl.set(lvl, caches);
27043 lvls.push(lvl);
27044 }
27045
27046 return caches;
27047 }
27048 }, {
27049 key: "getCache",
27050 value: function getCache(key, lvl) {
27051 return this.getCachesAt(lvl).get(key);
27052 }
27053 }, {
27054 key: "get",
27055 value: function get(ele, lvl) {
27056 var key = this.getKey(ele);
27057 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27058
27059 if (cache != null) {
27060 this.updateKeyMappingFor(ele);
27061 }
27062
27063 return cache;
27064 }
27065 }, {
27066 key: "getForCachedKey",
27067 value: function getForCachedKey(ele, lvl) {
27068 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27069
27070 var cache = this.getCache(key, lvl);
27071 return cache;
27072 }
27073 }, {
27074 key: "hasCache",
27075 value: function hasCache(key, lvl) {
27076 return this.getCachesAt(lvl).has(key);
27077 }
27078 }, {
27079 key: "has",
27080 value: function has(ele, lvl) {
27081 var key = this.getKey(ele);
27082 return this.hasCache(key, lvl);
27083 }
27084 }, {
27085 key: "setCache",
27086 value: function setCache(key, lvl, cache) {
27087 cache.key = key;
27088 this.getCachesAt(lvl).set(key, cache);
27089 }
27090 }, {
27091 key: "set",
27092 value: function set(ele, lvl, cache) {
27093 var key = this.getKey(ele);
27094 this.setCache(key, lvl, cache);
27095 this.updateKeyMappingFor(ele);
27096 }
27097 }, {
27098 key: "deleteCache",
27099 value: function deleteCache(key, lvl) {
27100 this.getCachesAt(lvl)["delete"](key);
27101 }
27102 }, {
27103 key: "delete",
27104 value: function _delete(ele, lvl) {
27105 var key = this.getKey(ele);
27106 this.deleteCache(key, lvl);
27107 }
27108 }, {
27109 key: "invalidateKey",
27110 value: function invalidateKey(key) {
27111 var _this = this;
27112
27113 this.lvls.forEach(function (lvl) {
27114 return _this.deleteCache(key, lvl);
27115 });
27116 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27117
27118 }, {
27119 key: "invalidate",
27120 value: function invalidate(ele) {
27121 var id = ele.id();
27122 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27123
27124 this.deleteKeyMappingFor(ele);
27125 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27126
27127 if (entireKeyInvalidated) {
27128 // clear mapping for current key
27129 this.invalidateKey(key);
27130 }
27131
27132 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27133 }
27134 }]);
27135
27136 return ElementTextureCacheLookup;
27137}();
27138
27139var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27140
27141var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27142
27143var minLvl = -4; // when scaling smaller than that we don't need to re-render
27144
27145var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27146
27147var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27148
27149var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27150
27151var defTxrWidth = 1024; // default/minimum texture width
27152
27153var maxTxrW = 1024; // the maximum width of a texture
27154
27155var maxTxrH = 1024; // the maximum height of a texture
27156
27157var minUtility = 0.2; // if usage of texture is less than this, it is retired
27158
27159var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27160
27161var maxFullnessChecks = 10; // dequeued after this many checks
27162
27163var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27164
27165var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27166
27167var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27168
27169var deqFastCost = 0.9; // % of frame time to be used when >60fps
27170
27171var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27172
27173var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27174
27175var getTxrReasons = {
27176 dequeue: 'dequeue',
27177 downscale: 'downscale',
27178 highQuality: 'highQuality'
27179};
27180var initDefaults = defaults({
27181 getKey: null,
27182 doesEleInvalidateKey: falsify,
27183 drawElement: null,
27184 getBoundingBox: null,
27185 getRotationPoint: null,
27186 getRotationOffset: null,
27187 isVisible: trueify,
27188 allowEdgeTxrCaching: true,
27189 allowParentTxrCaching: true
27190});
27191
27192var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27193 var self = this;
27194 self.renderer = renderer;
27195 self.onDequeues = [];
27196 var opts = initDefaults(initOptions);
27197 extend(self, opts);
27198 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27199 self.setupDequeueing();
27200};
27201
27202var ETCp = ElementTextureCache.prototype;
27203ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27204
27205ETCp.getTextureQueue = function (txrH) {
27206 var self = this;
27207 self.eleImgCaches = self.eleImgCaches || {};
27208 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27209}; // the list of usused textures which can be recycled (in use in texture queue)
27210
27211
27212ETCp.getRetiredTextureQueue = function (txrH) {
27213 var self = this;
27214 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27215 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27216 return rtxtrQ;
27217}; // queue of element draw requests at different scale levels
27218
27219
27220ETCp.getElementQueue = function () {
27221 var self = this;
27222 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
27223 return b.reqs - a.reqs;
27224 });
27225 return q;
27226}; // queue of element draw requests at different scale levels (element id lookup)
27227
27228
27229ETCp.getElementKeyToQueue = function () {
27230 var self = this;
27231 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27232 return k2q;
27233};
27234
27235ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27236 var self = this;
27237 var r = this.renderer;
27238 var zoom = r.cy.zoom();
27239 var lookup = this.lookup;
27240
27241 if (bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible()) {
27242 return null;
27243 }
27244
27245 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27246 return null;
27247 }
27248
27249 if (lvl == null) {
27250 lvl = Math.ceil(log2(zoom * pxRatio));
27251 }
27252
27253 if (lvl < minLvl) {
27254 lvl = minLvl;
27255 } else if (zoom >= maxZoom || lvl > maxLvl) {
27256 return null;
27257 }
27258
27259 var scale = Math.pow(2, lvl);
27260 var eleScaledH = bb.h * scale;
27261 var eleScaledW = bb.w * scale;
27262 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27263
27264 if (!this.isVisible(ele, scaledLabelShown)) {
27265 return null;
27266 }
27267
27268 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
27269
27270 if (eleCache && eleCache.invalidated) {
27271 eleCache.invalidated = false;
27272 eleCache.texture.invalidatedWidth -= eleCache.width;
27273 }
27274
27275 if (eleCache) {
27276 return eleCache;
27277 }
27278
27279 var txrH; // which texture height this ele belongs to
27280
27281 if (eleScaledH <= minTxrH) {
27282 txrH = minTxrH;
27283 } else if (eleScaledH <= txrStepH) {
27284 txrH = txrStepH;
27285 } else {
27286 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27287 }
27288
27289 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27290 return null; // caching large elements is not efficient
27291 }
27292
27293 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
27294
27295 var txr = txrQ[txrQ.length - 2];
27296
27297 var addNewTxr = function addNewTxr() {
27298 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27299 }; // try the last one if there is no second last one
27300
27301
27302 if (!txr) {
27303 txr = txrQ[txrQ.length - 1];
27304 } // if the last one doesn't exist, we need a first one
27305
27306
27307 if (!txr) {
27308 txr = addNewTxr();
27309 } // if there's no room in the current texture, we need a new one
27310
27311
27312 if (txr.width - txr.usedWidth < eleScaledW) {
27313 txr = addNewTxr();
27314 }
27315
27316 var scalableFrom = function scalableFrom(otherCache) {
27317 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27318 };
27319
27320 var deqing = reason && reason === getTxrReasons.dequeue;
27321 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27322 var downscaleReq = reason && reason === getTxrReasons.downscale;
27323 var higherCache; // the nearest cache with a higher level
27324
27325 for (var l = lvl + 1; l <= maxLvl; l++) {
27326 var c = lookup.get(ele, l);
27327
27328 if (c) {
27329 higherCache = c;
27330 break;
27331 }
27332 }
27333
27334 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27335
27336 var downscale = function downscale() {
27337 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27338 }; // reset ele area in texture
27339
27340
27341 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27342 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27343
27344 if (scalableFrom(oneUpCache)) {
27345 // then we can relatively cheaply rescale the existing image w/o rerendering
27346 downscale();
27347 } else if (scalableFrom(higherCache)) {
27348 // then use the higher cache for now and queue the next level down
27349 // to cheaply scale towards the smaller level
27350 if (highQualityReq) {
27351 for (var _l = higherCache.level; _l > lvl; _l--) {
27352 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27353 }
27354
27355 downscale();
27356 } else {
27357 self.queueElement(ele, higherCache.level - 1);
27358 return higherCache;
27359 }
27360 } else {
27361 var lowerCache; // the nearest cache with a lower level
27362
27363 if (!deqing && !highQualityReq && !downscaleReq) {
27364 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
27365 var _c = lookup.get(ele, _l2);
27366
27367 if (_c) {
27368 lowerCache = _c;
27369 break;
27370 }
27371 }
27372 }
27373
27374 if (scalableFrom(lowerCache)) {
27375 // then use the lower quality cache for now and queue the better one for later
27376 self.queueElement(ele, lvl);
27377 return lowerCache;
27378 }
27379
27380 txr.context.translate(txr.usedWidth, 0);
27381 txr.context.scale(scale, scale);
27382 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27383 txr.context.scale(1 / scale, 1 / scale);
27384 txr.context.translate(-txr.usedWidth, 0);
27385 }
27386
27387 eleCache = {
27388 x: txr.usedWidth,
27389 texture: txr,
27390 level: lvl,
27391 scale: scale,
27392 width: eleScaledW,
27393 height: eleScaledH,
27394 scaledLabelShown: scaledLabelShown
27395 };
27396 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27397 txr.eleCaches.push(eleCache);
27398 lookup.set(ele, lvl, eleCache);
27399 self.checkTextureFullness(txr);
27400 return eleCache;
27401};
27402
27403ETCp.invalidateElements = function (eles) {
27404 for (var i = 0; i < eles.length; i++) {
27405 this.invalidateElement(eles[i]);
27406 }
27407};
27408
27409ETCp.invalidateElement = function (ele) {
27410 var self = this;
27411 var lookup = self.lookup;
27412 var caches = [];
27413 var invalid = lookup.isInvalid(ele);
27414
27415 if (!invalid) {
27416 return; // override the invalidation request if the element key has not changed
27417 }
27418
27419 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
27420 var cache = lookup.getForCachedKey(ele, lvl);
27421
27422 if (cache) {
27423 caches.push(cache);
27424 }
27425 }
27426
27427 var noOtherElesUseCache = lookup.invalidate(ele);
27428
27429 if (noOtherElesUseCache) {
27430 for (var i = 0; i < caches.length; i++) {
27431 var _cache = caches[i];
27432 var txr = _cache.texture; // remove space from the texture it belongs to
27433
27434 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27435
27436 _cache.invalidated = true; // retire the texture if its utility is low
27437
27438 self.checkTextureUtility(txr);
27439 }
27440 } // remove from queue since the old req was for the old state
27441
27442
27443 self.removeFromQueue(ele);
27444};
27445
27446ETCp.checkTextureUtility = function (txr) {
27447 // invalidate all entries in the cache if the cache size is small
27448 if (txr.invalidatedWidth >= minUtility * txr.width) {
27449 this.retireTexture(txr);
27450 }
27451};
27452
27453ETCp.checkTextureFullness = function (txr) {
27454 // if texture has been mostly filled and passed over several times, remove
27455 // it from the queue so we don't need to waste time looking at it to put new things
27456 var self = this;
27457 var txrQ = self.getTextureQueue(txr.height);
27458
27459 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27460 removeFromArray(txrQ, txr);
27461 } else {
27462 txr.fullnessChecks++;
27463 }
27464};
27465
27466ETCp.retireTexture = function (txr) {
27467 var self = this;
27468 var txrH = txr.height;
27469 var txrQ = self.getTextureQueue(txrH);
27470 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27471
27472 removeFromArray(txrQ, txr);
27473 txr.retired = true; // remove the refs from the eles to the caches:
27474
27475 var eleCaches = txr.eleCaches;
27476
27477 for (var i = 0; i < eleCaches.length; i++) {
27478 var eleCache = eleCaches[i];
27479 lookup.deleteCache(eleCache.key, eleCache.level);
27480 }
27481
27482 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27483
27484 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27485 rtxtrQ.push(txr);
27486};
27487
27488ETCp.addTexture = function (txrH, minW) {
27489 var self = this;
27490 var txrQ = self.getTextureQueue(txrH);
27491 var txr = {};
27492 txrQ.push(txr);
27493 txr.eleCaches = [];
27494 txr.height = txrH;
27495 txr.width = Math.max(defTxrWidth, minW);
27496 txr.usedWidth = 0;
27497 txr.invalidatedWidth = 0;
27498 txr.fullnessChecks = 0;
27499 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27500 txr.context = txr.canvas.getContext('2d');
27501 return txr;
27502};
27503
27504ETCp.recycleTexture = function (txrH, minW) {
27505 var self = this;
27506 var txrQ = self.getTextureQueue(txrH);
27507 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27508
27509 for (var i = 0; i < rtxtrQ.length; i++) {
27510 var txr = rtxtrQ[i];
27511
27512 if (txr.width >= minW) {
27513 txr.retired = false;
27514 txr.usedWidth = 0;
27515 txr.invalidatedWidth = 0;
27516 txr.fullnessChecks = 0;
27517 clearArray(txr.eleCaches);
27518 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27519 txr.context.clearRect(0, 0, txr.width, txr.height);
27520 removeFromArray(rtxtrQ, txr);
27521 txrQ.push(txr);
27522 return txr;
27523 }
27524 }
27525};
27526
27527ETCp.queueElement = function (ele, lvl) {
27528 var self = this;
27529 var q = self.getElementQueue();
27530 var k2q = self.getElementKeyToQueue();
27531 var key = this.getKey(ele);
27532 var existingReq = k2q[key];
27533
27534 if (existingReq) {
27535 // use the max lvl b/c in between lvls are cheap to make
27536 existingReq.level = Math.max(existingReq.level, lvl);
27537 existingReq.eles.merge(ele);
27538 existingReq.reqs++;
27539 q.updateItem(existingReq);
27540 } else {
27541 var req = {
27542 eles: ele.spawn().merge(ele),
27543 level: lvl,
27544 reqs: 1,
27545 key: key
27546 };
27547 q.push(req);
27548 k2q[key] = req;
27549 }
27550};
27551
27552ETCp.dequeue = function (pxRatio
27553/*, extent*/
27554) {
27555 var self = this;
27556 var q = self.getElementQueue();
27557 var k2q = self.getElementKeyToQueue();
27558 var dequeued = [];
27559 var lookup = self.lookup;
27560
27561 for (var i = 0; i < maxDeqSize; i++) {
27562 if (q.size() > 0) {
27563 var req = q.pop();
27564 var key = req.key;
27565 var ele = req.eles[0]; // all eles have the same key
27566
27567 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27568
27569 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27570
27571 if (cacheExists) {
27572 continue;
27573 }
27574
27575 dequeued.push(req);
27576 var bb = self.getBoundingBox(ele);
27577 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27578 } else {
27579 break;
27580 }
27581 }
27582
27583 return dequeued;
27584};
27585
27586ETCp.removeFromQueue = function (ele) {
27587 var self = this;
27588 var q = self.getElementQueue();
27589 var k2q = self.getElementKeyToQueue();
27590 var key = this.getKey(ele);
27591 var req = k2q[key];
27592
27593 if (req != null) {
27594 if (req.eles.length === 1) {
27595 // remove if last ele in the req
27596 // bring to front of queue
27597 req.reqs = MAX_INT;
27598 q.updateItem(req);
27599 q.pop(); // remove from queue
27600
27601 k2q[key] = null; // remove from lookup map
27602 } else {
27603 // otherwise just remove ele from req
27604 req.eles.unmerge(ele);
27605 }
27606 }
27607};
27608
27609ETCp.onDequeue = function (fn) {
27610 this.onDequeues.push(fn);
27611};
27612
27613ETCp.offDequeue = function (fn) {
27614 removeFromArray(this.onDequeues, fn);
27615};
27616
27617ETCp.setupDequeueing = defs.setupDequeueing({
27618 deqRedrawThreshold: deqRedrawThreshold,
27619 deqCost: deqCost,
27620 deqAvgCost: deqAvgCost,
27621 deqNoDrawCost: deqNoDrawCost,
27622 deqFastCost: deqFastCost,
27623 deq: function deq(self, pxRatio, extent) {
27624 return self.dequeue(pxRatio, extent);
27625 },
27626 onDeqd: function onDeqd(self, deqd) {
27627 for (var i = 0; i < self.onDequeues.length; i++) {
27628 var fn = self.onDequeues[i];
27629 fn(deqd);
27630 }
27631 },
27632 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27633 for (var i = 0; i < deqd.length; i++) {
27634 var eles = deqd[i].eles;
27635
27636 for (var j = 0; j < eles.length; j++) {
27637 var bb = eles[j].boundingBox();
27638
27639 if (boundingBoxesIntersect(bb, extent)) {
27640 return true;
27641 }
27642 }
27643 }
27644
27645 return false;
27646 },
27647 priority: function priority(self) {
27648 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27649 }
27650});
27651
27652var defNumLayers = 1; // default number of layers to use
27653
27654var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27655
27656var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
27657
27658var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
27659
27660var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27661
27662var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27663
27664var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27665
27666var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27667
27668var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27669
27670var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27671
27672var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27673
27674var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27675
27676var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27677
27678var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27679// var log = function(){ console.log.apply( console, arguments ); };
27680
27681var LayeredTextureCache = function LayeredTextureCache(renderer) {
27682 var self = this;
27683 var r = self.renderer = renderer;
27684 var cy = r.cy;
27685 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27686
27687 self.firstGet = true;
27688 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27689 self.skipping = false;
27690 self.eleTxrDeqs = cy.collection();
27691 self.scheduleElementRefinement = util(function () {
27692 self.refineElementTextures(self.eleTxrDeqs);
27693 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27694 }, refineEleDebounceTime);
27695 r.beforeRender(function (willDraw, now) {
27696 if (now - self.lastInvalidationTime <= invalidThreshold) {
27697 self.skipping = true;
27698 } else {
27699 self.skipping = false;
27700 }
27701 }, r.beforeRenderPriorities.lyrTxrSkip);
27702
27703 var qSort = function qSort(a, b) {
27704 return b.reqs - a.reqs;
27705 };
27706
27707 self.layersQueue = new Heap(qSort);
27708 self.setupDequeueing();
27709};
27710
27711var LTCp = LayeredTextureCache.prototype;
27712var layerIdPool = 0;
27713var MAX_INT$1 = Math.pow(2, 53) - 1;
27714
27715LTCp.makeLayer = function (bb, lvl) {
27716 var scale = Math.pow(2, lvl);
27717 var w = Math.ceil(bb.w * scale);
27718 var h = Math.ceil(bb.h * scale);
27719 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27720 var layer = {
27721 id: layerIdPool = ++layerIdPool % MAX_INT$1,
27722 bb: bb,
27723 level: lvl,
27724 width: w,
27725 height: h,
27726 canvas: canvas,
27727 context: canvas.getContext('2d'),
27728 eles: [],
27729 elesQueue: [],
27730 reqs: 0
27731 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27732
27733 var cxt = layer.context;
27734 var dx = -layer.bb.x1;
27735 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
27736
27737 cxt.scale(scale, scale);
27738 cxt.translate(dx, dy);
27739 return layer;
27740};
27741
27742LTCp.getLayers = function (eles, pxRatio, lvl) {
27743 var self = this;
27744 var r = self.renderer;
27745 var cy = r.cy;
27746 var zoom = cy.zoom();
27747 var firstGet = self.firstGet;
27748 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
27749 //log eles.map(function(ele){ return ele.id() }) );
27750
27751 if (lvl == null) {
27752 lvl = Math.ceil(log2(zoom * pxRatio));
27753
27754 if (lvl < minLvl$1) {
27755 lvl = minLvl$1;
27756 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27757 return null;
27758 }
27759 }
27760
27761 self.validateLayersElesOrdering(lvl, eles);
27762 var layersByLvl = self.layersByLevel;
27763 var scale = Math.pow(2, lvl);
27764 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
27765 var bb;
27766 var lvlComplete = self.levelIsComplete(lvl, eles);
27767 var tmpLayers;
27768
27769 var checkTempLevels = function checkTempLevels() {
27770 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
27771 self.validateLayersElesOrdering(l, eles);
27772
27773 if (self.levelIsComplete(l, eles)) {
27774 tmpLayers = layersByLvl[l];
27775 return true;
27776 }
27777 };
27778
27779 var checkLvls = function checkLvls(dir) {
27780 if (tmpLayers) {
27781 return;
27782 }
27783
27784 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
27785 if (canUseAsTmpLvl(l)) {
27786 break;
27787 }
27788 }
27789 };
27790
27791 checkLvls(+1);
27792 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
27793
27794 for (var i = layers.length - 1; i >= 0; i--) {
27795 var layer = layers[i];
27796
27797 if (layer.invalid) {
27798 removeFromArray(layers, layer);
27799 }
27800 }
27801 };
27802
27803 if (!lvlComplete) {
27804 // if the current level is incomplete, then use the closest, best quality layerset temporarily
27805 // and later queue the current layerset so we can get the proper quality level soon
27806 checkTempLevels();
27807 } else {
27808 // log('level complete, using existing layers\n--');
27809 return layers;
27810 }
27811
27812 var getBb = function getBb() {
27813 if (!bb) {
27814 bb = makeBoundingBox();
27815
27816 for (var i = 0; i < eles.length; i++) {
27817 updateBoundingBox(bb, eles[i].boundingBox());
27818 }
27819 }
27820
27821 return bb;
27822 };
27823
27824 var makeLayer = function makeLayer(opts) {
27825 opts = opts || {};
27826 var after = opts.after;
27827 getBb();
27828 var area = bb.w * scale * (bb.h * scale);
27829
27830 if (area > maxLayerArea) {
27831 return null;
27832 }
27833
27834 var layer = self.makeLayer(bb, lvl);
27835
27836 if (after != null) {
27837 var index = layers.indexOf(after) + 1;
27838 layers.splice(index, 0, layer);
27839 } else if (opts.insert === undefined || opts.insert) {
27840 // no after specified => first layer made so put at start
27841 layers.unshift(layer);
27842 } // if( tmpLayers ){
27843 //self.queueLayer( layer );
27844 // }
27845
27846
27847 return layer;
27848 };
27849
27850 if (self.skipping && !firstGet) {
27851 // log('skip layers');
27852 return null;
27853 } // log('do layers');
27854
27855
27856 var layer = null;
27857 var maxElesPerLayer = eles.length / defNumLayers;
27858 var allowLazyQueueing = !firstGet;
27859
27860 for (var i = 0; i < eles.length; i++) {
27861 var ele = eles[i];
27862 var rs = ele._private.rscratch;
27863 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
27864
27865 var existingLayer = caches[lvl];
27866
27867 if (existingLayer) {
27868 // reuse layer for later eles
27869 // log('reuse layer for', ele.id());
27870 layer = existingLayer;
27871 continue;
27872 }
27873
27874 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
27875 // log('make new layer for ele %s', ele.id());
27876 layer = makeLayer({
27877 insert: true,
27878 after: layer
27879 }); // if now layer can be built then we can't use layers at this level
27880
27881 if (!layer) {
27882 return null;
27883 } // log('new layer with id %s', layer.id);
27884
27885 }
27886
27887 if (tmpLayers || allowLazyQueueing) {
27888 // log('queue ele %s in layer %s', ele.id(), layer.id);
27889 self.queueLayer(layer, ele);
27890 } else {
27891 // log('draw ele %s in layer %s', ele.id(), layer.id);
27892 self.drawEleInLayer(layer, ele, lvl, pxRatio);
27893 }
27894
27895 layer.eles.push(ele);
27896 caches[lvl] = layer;
27897 } // log('--');
27898
27899
27900 if (tmpLayers) {
27901 // then we only queued the current layerset and can't draw it yet
27902 return tmpLayers;
27903 }
27904
27905 if (allowLazyQueueing) {
27906 // log('lazy queue level', lvl);
27907 return null;
27908 }
27909
27910 return layers;
27911}; // a layer may want to use an ele cache of a higher level to avoid blurriness
27912// so the layer level might not equal the ele level
27913
27914
27915LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
27916 return lvl;
27917};
27918
27919LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
27920 var self = this;
27921 var r = this.renderer;
27922 var context = layer.context;
27923 var bb = ele.boundingBox();
27924
27925 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
27926 return;
27927 }
27928
27929 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
27930
27931 {
27932 r.setImgSmoothing(context, false);
27933 }
27934
27935 {
27936 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
27937 }
27938
27939 {
27940 r.setImgSmoothing(context, true);
27941 }
27942};
27943
27944LTCp.levelIsComplete = function (lvl, eles) {
27945 var self = this;
27946 var layers = self.layersByLevel[lvl];
27947
27948 if (!layers || layers.length === 0) {
27949 return false;
27950 }
27951
27952 var numElesInLayers = 0;
27953
27954 for (var i = 0; i < layers.length; i++) {
27955 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
27956
27957 if (layer.reqs > 0) {
27958 return false;
27959 } // if the layer is invalid, the level is not complete
27960
27961
27962 if (layer.invalid) {
27963 return false;
27964 }
27965
27966 numElesInLayers += layer.eles.length;
27967 } // we should have exactly the number of eles passed in to be complete
27968
27969
27970 if (numElesInLayers !== eles.length) {
27971 return false;
27972 }
27973
27974 return true;
27975};
27976
27977LTCp.validateLayersElesOrdering = function (lvl, eles) {
27978 var layers = this.layersByLevel[lvl];
27979
27980 if (!layers) {
27981 return;
27982 } // if in a layer the eles are not in the same order, then the layer is invalid
27983 // (i.e. there is an ele in between the eles in the layer)
27984
27985
27986 for (var i = 0; i < layers.length; i++) {
27987 var layer = layers[i];
27988 var offset = -1; // find the offset
27989
27990 for (var j = 0; j < eles.length; j++) {
27991 if (layer.eles[0] === eles[j]) {
27992 offset = j;
27993 break;
27994 }
27995 }
27996
27997 if (offset < 0) {
27998 // then the layer has nonexistant elements and is invalid
27999 this.invalidateLayer(layer);
28000 continue;
28001 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28002
28003
28004 var o = offset;
28005
28006 for (var j = 0; j < layer.eles.length; j++) {
28007 if (layer.eles[j] !== eles[o + j]) {
28008 // log('invalidate based on ordering', layer.id);
28009 this.invalidateLayer(layer);
28010 break;
28011 }
28012 }
28013 }
28014};
28015
28016LTCp.updateElementsInLayers = function (eles, update) {
28017 var self = this;
28018 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28019 // layer itself along the way
28020
28021 for (var i = 0; i < eles.length; i++) {
28022 var req = isEles ? null : eles[i];
28023 var ele = isEles ? eles[i] : eles[i].ele;
28024 var rs = ele._private.rscratch;
28025 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28026
28027 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28028 var layer = caches[l];
28029
28030 if (!layer) {
28031 continue;
28032 } // if update is a request from the ele cache, then it affects only
28033 // the matching level
28034
28035
28036 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28037 continue;
28038 }
28039
28040 update(layer, ele, req);
28041 }
28042 }
28043};
28044
28045LTCp.haveLayers = function () {
28046 var self = this;
28047 var haveLayers = false;
28048
28049 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28050 var layers = self.layersByLevel[l];
28051
28052 if (layers && layers.length > 0) {
28053 haveLayers = true;
28054 break;
28055 }
28056 }
28057
28058 return haveLayers;
28059};
28060
28061LTCp.invalidateElements = function (eles) {
28062 var self = this;
28063
28064 if (eles.length === 0) {
28065 return;
28066 }
28067
28068 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28069
28070 if (eles.length === 0 || !self.haveLayers()) {
28071 return;
28072 }
28073
28074 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28075 self.invalidateLayer(layer);
28076 });
28077};
28078
28079LTCp.invalidateLayer = function (layer) {
28080 // log('update invalidate layer time');
28081 this.lastInvalidationTime = performanceNow();
28082
28083 if (layer.invalid) {
28084 return;
28085 } // save cycles
28086
28087
28088 var lvl = layer.level;
28089 var eles = layer.eles;
28090 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28091
28092 removeFromArray(layers, layer); // layer.eles = [];
28093
28094 layer.elesQueue = [];
28095 layer.invalid = true;
28096
28097 if (layer.replacement) {
28098 layer.replacement.invalid = true;
28099 }
28100
28101 for (var i = 0; i < eles.length; i++) {
28102 var caches = eles[i]._private.rscratch.imgLayerCaches;
28103
28104 if (caches) {
28105 caches[lvl] = null;
28106 }
28107 }
28108};
28109
28110LTCp.refineElementTextures = function (eles) {
28111 var self = this; // log('refine', eles.length);
28112
28113 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28114 var rLyr = layer.replacement;
28115
28116 if (!rLyr) {
28117 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28118 rLyr.replaces = layer;
28119 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28120 }
28121
28122 if (!rLyr.reqs) {
28123 for (var i = 0; i < rLyr.eles.length; i++) {
28124 self.queueLayer(rLyr, rLyr.eles[i]);
28125 } // log('queue replacement layer refinement', rLyr.id);
28126
28127 }
28128 });
28129};
28130
28131LTCp.enqueueElementRefinement = function (ele) {
28132
28133 this.eleTxrDeqs.merge(ele);
28134 this.scheduleElementRefinement();
28135};
28136
28137LTCp.queueLayer = function (layer, ele) {
28138 var self = this;
28139 var q = self.layersQueue;
28140 var elesQ = layer.elesQueue;
28141 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28142
28143 if (layer.replacement) {
28144 return;
28145 }
28146
28147 if (ele) {
28148 if (hasId[ele.id()]) {
28149 return;
28150 }
28151
28152 elesQ.push(ele);
28153 hasId[ele.id()] = true;
28154 }
28155
28156 if (layer.reqs) {
28157 layer.reqs++;
28158 q.updateItem(layer);
28159 } else {
28160 layer.reqs = 1;
28161 q.push(layer);
28162 }
28163};
28164
28165LTCp.dequeue = function (pxRatio) {
28166 var self = this;
28167 var q = self.layersQueue;
28168 var deqd = [];
28169 var eleDeqs = 0;
28170
28171 while (eleDeqs < maxDeqSize$1) {
28172 if (q.size() === 0) {
28173 break;
28174 }
28175
28176 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28177
28178 if (layer.replacement) {
28179 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28180 q.pop();
28181 continue;
28182 } // if this is a replacement layer that has been superceded, then forget it
28183
28184
28185 if (layer.replaces && layer !== layer.replaces.replacement) {
28186 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28187 q.pop();
28188 continue;
28189 }
28190
28191 if (layer.invalid) {
28192 // log('replacement layer %s is invalid; dequeued', layer.id);
28193 q.pop();
28194 continue;
28195 }
28196
28197 var ele = layer.elesQueue.shift();
28198
28199 if (ele) {
28200 // log('dequeue layer %s', layer.id);
28201 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28202 eleDeqs++;
28203 }
28204
28205 if (deqd.length === 0) {
28206 // we need only one entry in deqd to queue redrawing etc
28207 deqd.push(true);
28208 } // if the layer has all its eles done, then remove from the queue
28209
28210
28211 if (layer.elesQueue.length === 0) {
28212 q.pop();
28213 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28214 // when a replacement layer is dequeued, it replaces the old layer in the level
28215
28216 if (layer.replaces) {
28217 self.applyLayerReplacement(layer);
28218 }
28219
28220 self.requestRedraw();
28221 }
28222 }
28223
28224 return deqd;
28225};
28226
28227LTCp.applyLayerReplacement = function (layer) {
28228 var self = this;
28229 var layersInLevel = self.layersByLevel[layer.level];
28230 var replaced = layer.replaces;
28231 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28232 // refs would be a mistake (i.e. overwriting the true active layer)
28233
28234 if (index < 0 || replaced.invalid) {
28235 // log('replacement layer would have no effect', layer.id);
28236 return;
28237 }
28238
28239 layersInLevel[index] = layer; // replace level ref
28240 // replace refs in eles
28241
28242 for (var i = 0; i < layer.eles.length; i++) {
28243 var _p = layer.eles[i]._private;
28244 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28245
28246 if (cache) {
28247 cache[layer.level] = layer;
28248 }
28249 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
28250
28251
28252 self.requestRedraw();
28253};
28254
28255LTCp.requestRedraw = util(function () {
28256 var r = this.renderer;
28257 r.redrawHint('eles', true);
28258 r.redrawHint('drag', true);
28259 r.redraw();
28260}, 100);
28261LTCp.setupDequeueing = defs.setupDequeueing({
28262 deqRedrawThreshold: deqRedrawThreshold$1,
28263 deqCost: deqCost$1,
28264 deqAvgCost: deqAvgCost$1,
28265 deqNoDrawCost: deqNoDrawCost$1,
28266 deqFastCost: deqFastCost$1,
28267 deq: function deq(self, pxRatio) {
28268 return self.dequeue(pxRatio);
28269 },
28270 onDeqd: noop,
28271 shouldRedraw: trueify,
28272 priority: function priority(self) {
28273 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28274 }
28275});
28276
28277var CRp = {};
28278var impl;
28279
28280function polygon(context, points) {
28281 for (var i = 0; i < points.length; i++) {
28282 var pt = points[i];
28283 context.lineTo(pt.x, pt.y);
28284 }
28285}
28286
28287function triangleBackcurve(context, points, controlPoint) {
28288 var firstPt;
28289
28290 for (var i = 0; i < points.length; i++) {
28291 var pt = points[i];
28292
28293 if (i === 0) {
28294 firstPt = pt;
28295 }
28296
28297 context.lineTo(pt.x, pt.y);
28298 }
28299
28300 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28301}
28302
28303function triangleTee(context, trianglePoints, teePoints) {
28304 if (context.beginPath) {
28305 context.beginPath();
28306 }
28307
28308 var triPts = trianglePoints;
28309
28310 for (var i = 0; i < triPts.length; i++) {
28311 var pt = triPts[i];
28312 context.lineTo(pt.x, pt.y);
28313 }
28314
28315 var teePts = teePoints;
28316 var firstTeePt = teePoints[0];
28317 context.moveTo(firstTeePt.x, firstTeePt.y);
28318
28319 for (var i = 1; i < teePts.length; i++) {
28320 var pt = teePts[i];
28321 context.lineTo(pt.x, pt.y);
28322 }
28323
28324 if (context.closePath) {
28325 context.closePath();
28326 }
28327}
28328
28329function circle(context, rx, ry, r) {
28330 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28331}
28332
28333CRp.arrowShapeImpl = function (name) {
28334 return (impl || (impl = {
28335 'polygon': polygon,
28336 'triangle-backcurve': triangleBackcurve,
28337 'triangle-tee': triangleTee,
28338 'triangle-cross': triangleTee,
28339 'circle': circle
28340 }))[name];
28341};
28342
28343var CRp$1 = {};
28344
28345CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28346 var r = this;
28347
28348 if (ele.isNode()) {
28349 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28350 } else {
28351 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28352 }
28353};
28354
28355CRp$1.drawElementOverlay = function (context, ele) {
28356 var r = this;
28357
28358 if (ele.isNode()) {
28359 r.drawNodeOverlay(context, ele);
28360 } else {
28361 r.drawEdgeOverlay(context, ele);
28362 }
28363};
28364
28365CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28366 var r = this;
28367 var bb = eleTxrCache.getBoundingBox(ele);
28368
28369 if (bb.w === 0 || bb.h === 0) {
28370 return;
28371 } // ignore zero size case
28372
28373
28374 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28375
28376 if (eleCache != null) {
28377 var opacity = getOpacity(r, ele);
28378
28379 if (opacity === 0) {
28380 return;
28381 }
28382
28383 var theta = getRotation(r, ele);
28384 var x1 = bb.x1,
28385 y1 = bb.y1,
28386 w = bb.w,
28387 h = bb.h;
28388 var x, y, sx, sy, smooth;
28389
28390 if (theta !== 0) {
28391 var rotPt = eleTxrCache.getRotationPoint(ele);
28392 sx = rotPt.x;
28393 sy = rotPt.y;
28394 context.translate(sx, sy);
28395 context.rotate(theta);
28396 smooth = r.getImgSmoothing(context);
28397
28398 if (!smooth) {
28399 r.setImgSmoothing(context, true);
28400 }
28401
28402 var off = eleTxrCache.getRotationOffset(ele);
28403 x = off.x;
28404 y = off.y;
28405 } else {
28406 x = x1;
28407 y = y1;
28408 }
28409
28410 var oldGlobalAlpha;
28411
28412 if (opacity !== 1) {
28413 oldGlobalAlpha = context.globalAlpha;
28414 context.globalAlpha = oldGlobalAlpha * opacity;
28415 }
28416
28417 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28418
28419 if (opacity !== 1) {
28420 context.globalAlpha = oldGlobalAlpha;
28421 }
28422
28423 if (theta !== 0) {
28424 context.rotate(-theta);
28425 context.translate(-sx, -sy);
28426
28427 if (!smooth) {
28428 r.setImgSmoothing(context, false);
28429 }
28430 }
28431 } else {
28432 eleTxrCache.drawElement(context, ele); // direct draw fallback
28433 }
28434};
28435
28436var getZeroRotation = function getZeroRotation() {
28437 return 0;
28438};
28439
28440var getLabelRotation = function getLabelRotation(r, ele) {
28441 return r.getTextAngle(ele, null);
28442};
28443
28444var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28445 return r.getTextAngle(ele, 'source');
28446};
28447
28448var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28449 return r.getTextAngle(ele, 'target');
28450};
28451
28452var getOpacity = function getOpacity(r, ele) {
28453 return ele.effectiveOpacity();
28454};
28455
28456var getTextOpacity = function getTextOpacity(e, ele) {
28457 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28458};
28459
28460CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28461 var r = this;
28462 var _r$data = r.data,
28463 eleTxrCache = _r$data.eleTxrCache,
28464 lblTxrCache = _r$data.lblTxrCache,
28465 slbTxrCache = _r$data.slbTxrCache,
28466 tlbTxrCache = _r$data.tlbTxrCache;
28467 var bb = ele.boundingBox();
28468 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28469
28470 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28471 return;
28472 }
28473
28474 if (!extent || boundingBoxesIntersect(bb, extent)) {
28475 var isEdge = ele.isEdge();
28476
28477 var badLine = ele.element()._private.rscratch.badLine;
28478
28479 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28480
28481 if (!isEdge || !badLine) {
28482 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28483 }
28484
28485 if (isEdge && !badLine) {
28486 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28487 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28488 }
28489
28490 r.drawElementOverlay(context, ele);
28491 }
28492};
28493
28494CRp$1.drawElements = function (context, eles) {
28495 var r = this;
28496
28497 for (var i = 0; i < eles.length; i++) {
28498 var ele = eles[i];
28499 r.drawElement(context, ele);
28500 }
28501};
28502
28503CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
28504 var r = this;
28505
28506 for (var i = 0; i < eles.length; i++) {
28507 var ele = eles[i];
28508 r.drawCachedElement(context, ele, pxRatio, extent);
28509 }
28510};
28511
28512CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
28513 var r = this;
28514
28515 for (var i = 0; i < eles.length; i++) {
28516 var ele = eles[i];
28517
28518 if (!ele.isNode()) {
28519 continue;
28520 }
28521
28522 r.drawCachedElement(context, ele, pxRatio, extent);
28523 }
28524};
28525
28526CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
28527 var r = this;
28528 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28529
28530 if (layers) {
28531 for (var i = 0; i < layers.length; i++) {
28532 var layer = layers[i];
28533 var bb = layer.bb;
28534
28535 if (bb.w === 0 || bb.h === 0) {
28536 continue;
28537 }
28538
28539 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28540 }
28541 } else {
28542 // fall back on plain caching if no layers
28543 r.drawCachedElements(context, eles, pxRatio, extent);
28544 }
28545};
28546
28547/* global Path2D */
28548var CRp$2 = {};
28549
28550CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
28551 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28552 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28553 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28554 var r = this;
28555 var rs = edge._private.rscratch;
28556
28557 if (shouldDrawOpacity && !edge.visible()) {
28558 return;
28559 } // if bezier ctrl pts can not be calculated, then die
28560
28561
28562 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28563 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28564 return;
28565 }
28566
28567 var bb;
28568
28569 if (shiftToOriginWithBb) {
28570 bb = shiftToOriginWithBb;
28571 context.translate(-bb.x1, -bb.y1);
28572 }
28573
28574 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28575 var lineStyle = edge.pstyle('line-style').value;
28576 var edgeWidth = edge.pstyle('width').pfValue;
28577 var lineCap = edge.pstyle('line-cap').value;
28578
28579 var drawLine = function drawLine() {
28580 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
28581 context.lineWidth = edgeWidth;
28582 context.lineCap = lineCap;
28583 r.eleStrokeStyle(context, edge, strokeOpacity);
28584 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28585 context.lineCap = 'butt'; // reset for other drawing functions
28586 };
28587
28588 var drawOverlay = function drawOverlay() {
28589 if (!shouldDrawOverlay) {
28590 return;
28591 }
28592
28593 r.drawEdgeOverlay(context, edge);
28594 };
28595
28596 var drawArrows = function drawArrows() {
28597 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : opacity;
28598 r.drawArrowheads(context, edge, arrowOpacity);
28599 };
28600
28601 var drawText = function drawText() {
28602 r.drawElementText(context, edge, null, drawLabel);
28603 };
28604
28605 context.lineJoin = 'round';
28606 var ghost = edge.pstyle('ghost').value === 'yes';
28607
28608 if (ghost) {
28609 var gx = edge.pstyle('ghost-offset-x').pfValue;
28610 var gy = edge.pstyle('ghost-offset-y').pfValue;
28611 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28612 var effectiveGhostOpacity = opacity * ghostOpacity;
28613 context.translate(gx, gy);
28614 drawLine(effectiveGhostOpacity);
28615 drawArrows(effectiveGhostOpacity);
28616 context.translate(-gx, -gy);
28617 }
28618
28619 drawLine();
28620 drawArrows();
28621 drawOverlay();
28622 drawText();
28623
28624 if (shiftToOriginWithBb) {
28625 context.translate(bb.x1, bb.y1);
28626 }
28627};
28628
28629CRp$2.drawEdgeOverlay = function (context, edge) {
28630 if (!edge.visible()) {
28631 return;
28632 }
28633
28634 var overlayOpacity = edge.pstyle('overlay-opacity').value;
28635
28636 if (overlayOpacity === 0) {
28637 return;
28638 }
28639
28640 var r = this;
28641 var usePaths = r.usePaths();
28642 var rs = edge._private.rscratch;
28643 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
28644 var overlayWidth = 2 * overlayPadding;
28645 var overlayColor = edge.pstyle('overlay-color').value;
28646 context.lineWidth = overlayWidth;
28647
28648 if (rs.edgeType === 'self' && !usePaths) {
28649 context.lineCap = 'butt';
28650 } else {
28651 context.lineCap = 'round';
28652 }
28653
28654 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
28655 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28656};
28657
28658CRp$2.drawEdgePath = function (edge, context, pts, type) {
28659 var rs = edge._private.rscratch;
28660 var canvasCxt = context;
28661 var path;
28662 var pathCacheHit = false;
28663 var usePaths = this.usePaths();
28664 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28665 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28666
28667 if (usePaths) {
28668 var pathCacheKey = pts.join('$');
28669 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28670
28671 if (keyMatches) {
28672 path = context = rs.pathCache;
28673 pathCacheHit = true;
28674 } else {
28675 path = context = new Path2D();
28676 rs.pathCacheKey = pathCacheKey;
28677 rs.pathCache = path;
28678 }
28679 }
28680
28681 if (canvasCxt.setLineDash) {
28682 // for very outofdate browsers
28683 switch (type) {
28684 case 'dotted':
28685 canvasCxt.setLineDash([1, 1]);
28686 break;
28687
28688 case 'dashed':
28689 canvasCxt.setLineDash(lineDashPattern);
28690 canvasCxt.lineDashOffset = lineDashOffset;
28691 break;
28692
28693 case 'solid':
28694 canvasCxt.setLineDash([]);
28695 break;
28696 }
28697 }
28698
28699 if (!pathCacheHit && !rs.badLine) {
28700 if (context.beginPath) {
28701 context.beginPath();
28702 }
28703
28704 context.moveTo(pts[0], pts[1]);
28705
28706 switch (rs.edgeType) {
28707 case 'bezier':
28708 case 'self':
28709 case 'compound':
28710 case 'multibezier':
28711 for (var i = 2; i + 3 < pts.length; i += 4) {
28712 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
28713 }
28714
28715 break;
28716
28717 case 'straight':
28718 case 'segments':
28719 case 'haystack':
28720 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
28721 context.lineTo(pts[_i], pts[_i + 1]);
28722 }
28723
28724 break;
28725 }
28726 }
28727
28728 context = canvasCxt;
28729
28730 if (usePaths) {
28731 context.stroke(path);
28732 } else {
28733 context.stroke();
28734 } // reset any line dashes
28735
28736
28737 if (context.setLineDash) {
28738 // for very outofdate browsers
28739 context.setLineDash([]);
28740 }
28741};
28742
28743CRp$2.drawArrowheads = function (context, edge, opacity) {
28744 var rs = edge._private.rscratch;
28745 var isHaystack = rs.edgeType === 'haystack';
28746
28747 if (!isHaystack) {
28748 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
28749 }
28750
28751 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
28752 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
28753
28754 if (!isHaystack) {
28755 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
28756 }
28757};
28758
28759CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
28760 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
28761 return;
28762 }
28763
28764 var self = this;
28765 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
28766
28767 if (arrowShape === 'none') {
28768 return;
28769 }
28770
28771 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
28772 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
28773 var edgeWidth = edge.pstyle('width').pfValue;
28774 var edgeOpacity = edge.pstyle('opacity').value;
28775
28776 if (opacity === undefined) {
28777 opacity = edgeOpacity;
28778 }
28779
28780 var gco = context.globalCompositeOperation;
28781
28782 if (opacity !== 1 || arrowFill === 'hollow') {
28783 // then extra clear is needed
28784 context.globalCompositeOperation = 'destination-out';
28785 self.colorFillStyle(context, 255, 255, 255, 1);
28786 self.colorStrokeStyle(context, 255, 255, 255, 1);
28787 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
28788 context.globalCompositeOperation = gco;
28789 } // otherwise, the opaque arrow clears it for free :)
28790
28791
28792 var color = edge.pstyle(prefix + '-arrow-color').value;
28793 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
28794 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28795 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
28796};
28797
28798CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
28799 var r = this;
28800 var usePaths = this.usePaths() && shape !== 'triangle-cross';
28801 var pathCacheHit = false;
28802 var path;
28803 var canvasContext = context;
28804 var translation = {
28805 x: x,
28806 y: y
28807 };
28808 var scale = edge.pstyle('arrow-scale').value;
28809 var size = this.getArrowWidth(edgeWidth, scale);
28810 var shapeImpl = r.arrowShapes[shape];
28811
28812 if (usePaths) {
28813 var cache = r.arrowPathCache = r.arrowPathCache || [];
28814 var key = hashString(shape);
28815 var cachedPath = cache[key];
28816
28817 if (cachedPath != null) {
28818 path = context = cachedPath;
28819 pathCacheHit = true;
28820 } else {
28821 path = context = new Path2D();
28822 cache[key] = path;
28823 }
28824 }
28825
28826 if (!pathCacheHit) {
28827 if (context.beginPath) {
28828 context.beginPath();
28829 }
28830
28831 if (usePaths) {
28832 // store in the path cache with values easily manipulated later
28833 shapeImpl.draw(context, 1, 0, {
28834 x: 0,
28835 y: 0
28836 }, 1);
28837 } else {
28838 shapeImpl.draw(context, size, angle, translation, edgeWidth);
28839 }
28840
28841 if (context.closePath) {
28842 context.closePath();
28843 }
28844 }
28845
28846 context = canvasContext;
28847
28848 if (usePaths) {
28849 // set transform to arrow position/orientation
28850 context.translate(x, y);
28851 context.rotate(angle);
28852 context.scale(size, size);
28853 }
28854
28855 if (fill === 'filled' || fill === 'both') {
28856 if (usePaths) {
28857 context.fill(path);
28858 } else {
28859 context.fill();
28860 }
28861 }
28862
28863 if (fill === 'hollow' || fill === 'both') {
28864 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
28865 context.lineJoin = 'miter';
28866
28867 if (usePaths) {
28868 context.stroke(path);
28869 } else {
28870 context.stroke();
28871 }
28872 }
28873
28874 if (usePaths) {
28875 // reset transform by applying inverse
28876 context.scale(1 / size, 1 / size);
28877 context.rotate(-angle);
28878 context.translate(-x, -y);
28879 }
28880};
28881
28882var CRp$3 = {};
28883
28884CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
28885 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
28886 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
28887 return;
28888 }
28889
28890 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
28891};
28892
28893CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
28894 var r = this;
28895 var pos = node.position();
28896 var nodeX = pos.x;
28897 var nodeY = pos.y;
28898 var styleObj = node.cy().style();
28899 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
28900 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
28901 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
28902 var nodeW = node.width();
28903 var nodeH = node.height();
28904 var paddingX2 = node.padding() * 2;
28905 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28906 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
28907 var rs = node._private.rscratch;
28908 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
28909 var shouldClip = clip === 'node';
28910 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
28911 var imgW = img.width || img.cachedW;
28912 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
28913
28914 if (null == imgW || null == imgH) {
28915 document.body.appendChild(img); // eslint-disable-line no-undef
28916
28917 imgW = img.cachedW = img.width || img.offsetWidth;
28918 imgH = img.cachedH = img.height || img.offsetHeight;
28919 document.body.removeChild(img); // eslint-disable-line no-undef
28920 }
28921
28922 var w = imgW;
28923 var h = imgH;
28924
28925 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
28926 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
28927 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
28928 } else {
28929 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
28930 }
28931 }
28932
28933 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
28934 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
28935 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
28936 } else {
28937 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
28938 }
28939 }
28940
28941 if (w === 0 || h === 0) {
28942 return; // no point in drawing empty image (and chrome is broken in this case)
28943 }
28944
28945 if (fit === 'contain') {
28946 var scale = Math.min(nodeTW / w, nodeTH / h);
28947 w *= scale;
28948 h *= scale;
28949 } else if (fit === 'cover') {
28950 var scale = Math.max(nodeTW / w, nodeTH / h);
28951 w *= scale;
28952 h *= scale;
28953 }
28954
28955 var x = nodeX - nodeTW / 2; // left
28956
28957 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
28958 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
28959
28960 if (posXUnits === '%') {
28961 x += (nodeTW - w) * posXPfVal;
28962 } else {
28963 x += posXPfVal;
28964 }
28965
28966 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
28967 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
28968
28969 if (offXUnits === '%') {
28970 x += (nodeTW - w) * offXPfVal;
28971 } else {
28972 x += offXPfVal;
28973 }
28974
28975 var y = nodeY - nodeTH / 2; // top
28976
28977 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
28978 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
28979
28980 if (posYUnits === '%') {
28981 y += (nodeTH - h) * posYPfVal;
28982 } else {
28983 y += posYPfVal;
28984 }
28985
28986 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
28987 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
28988
28989 if (offYUnits === '%') {
28990 y += (nodeTH - h) * offYPfVal;
28991 } else {
28992 y += offYPfVal;
28993 }
28994
28995 if (rs.pathCache) {
28996 x -= nodeX;
28997 y -= nodeY;
28998 nodeX = 0;
28999 nodeY = 0;
29000 }
29001
29002 var gAlpha = context.globalAlpha;
29003 context.globalAlpha = imgOpacity;
29004
29005 if (repeat === 'no-repeat') {
29006 if (shouldClip) {
29007 context.save();
29008
29009 if (rs.pathCache) {
29010 context.clip(rs.pathCache);
29011 } else {
29012 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29013 context.clip();
29014 }
29015 }
29016
29017 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29018
29019 if (shouldClip) {
29020 context.restore();
29021 }
29022 } else {
29023 var pattern = context.createPattern(img, repeat);
29024 context.fillStyle = pattern;
29025 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29026 context.translate(x, y);
29027 context.fill();
29028 context.translate(-x, -y);
29029 }
29030
29031 context.globalAlpha = gAlpha;
29032};
29033
29034var CRp$4 = {};
29035
29036CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29037 if (!scale) {
29038 var zoom = ele.cy().zoom();
29039 var pxRatio = this.getPixelRatio();
29040 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29041
29042 scale = Math.pow(2, lvl);
29043 }
29044
29045 var computedSize = ele.pstyle('font-size').pfValue * scale;
29046 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29047
29048 if (computedSize < minSize) {
29049 return false;
29050 }
29051
29052 return true;
29053};
29054
29055CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29056 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29057 var r = this;
29058
29059 if (force == null) {
29060 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29061 return;
29062 }
29063 } else if (force === false) {
29064 return;
29065 }
29066
29067 if (ele.isNode()) {
29068 var label = ele.pstyle('label');
29069
29070 if (!label || !label.value) {
29071 return;
29072 }
29073
29074 var justification = r.getLabelJustification(ele);
29075 context.textAlign = justification;
29076 context.textBaseline = 'bottom';
29077 } else {
29078 var badLine = ele.element()._private.rscratch.badLine;
29079
29080 var _label = ele.pstyle('label');
29081
29082 var srcLabel = ele.pstyle('source-label');
29083 var tgtLabel = ele.pstyle('target-label');
29084
29085 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29086 return;
29087 }
29088
29089 context.textAlign = 'center';
29090 context.textBaseline = 'bottom';
29091 }
29092
29093 var applyRotation = !shiftToOriginWithBb;
29094 var bb;
29095
29096 if (shiftToOriginWithBb) {
29097 bb = shiftToOriginWithBb;
29098 context.translate(-bb.x1, -bb.y1);
29099 }
29100
29101 if (prefix == null) {
29102 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29103
29104 if (ele.isEdge()) {
29105 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29106 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29107 }
29108 } else {
29109 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29110 }
29111
29112 if (shiftToOriginWithBb) {
29113 context.translate(bb.x1, bb.y1);
29114 }
29115};
29116
29117CRp$4.getFontCache = function (context) {
29118 var cache;
29119 this.fontCaches = this.fontCaches || [];
29120
29121 for (var i = 0; i < this.fontCaches.length; i++) {
29122 cache = this.fontCaches[i];
29123
29124 if (cache.context === context) {
29125 return cache;
29126 }
29127 }
29128
29129 cache = {
29130 context: context
29131 };
29132 this.fontCaches.push(cache);
29133 return cache;
29134}; // set up canvas context with font
29135// returns transformed text string
29136
29137
29138CRp$4.setupTextStyle = function (context, ele) {
29139 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29140 // Font style
29141 var labelStyle = ele.pstyle('font-style').strValue;
29142 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29143 var labelFamily = ele.pstyle('font-family').strValue;
29144 var labelWeight = ele.pstyle('font-weight').strValue;
29145 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29146 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29147 var color = ele.pstyle('color').value;
29148 var outlineColor = ele.pstyle('text-outline-color').value;
29149 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29150 context.lineJoin = 'round'; // so text outlines aren't jagged
29151
29152 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29153 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29154}; // TODO ensure re-used
29155
29156
29157function roundRect(ctx, x, y, width, height) {
29158 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29159 ctx.beginPath();
29160 ctx.moveTo(x + radius, y);
29161 ctx.lineTo(x + width - radius, y);
29162 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29163 ctx.lineTo(x + width, y + height - radius);
29164 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29165 ctx.lineTo(x + radius, y + height);
29166 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29167 ctx.lineTo(x, y + radius);
29168 ctx.quadraticCurveTo(x, y, x + radius, y);
29169 ctx.closePath();
29170 ctx.fill();
29171}
29172
29173CRp$4.getTextAngle = function (ele, prefix) {
29174 var theta;
29175 var _p = ele._private;
29176 var rscratch = _p.rscratch;
29177 var pdash = prefix ? prefix + '-' : '';
29178 var rotation = ele.pstyle(pdash + 'text-rotation');
29179 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29180
29181 if (rotation.strValue === 'autorotate') {
29182 theta = ele.isEdge() ? textAngle : 0;
29183 } else if (rotation.strValue === 'none') {
29184 theta = 0;
29185 } else {
29186 theta = rotation.pfValue;
29187 }
29188
29189 return theta;
29190};
29191
29192CRp$4.drawText = function (context, ele, prefix) {
29193 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29194 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29195 var _p = ele._private;
29196 var rscratch = _p.rscratch;
29197 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29198
29199 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29200 return;
29201 } // use 'main' as an alias for the main label (i.e. null prefix)
29202
29203
29204 if (prefix === 'main') {
29205 prefix = null;
29206 }
29207
29208 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29209 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29210 var orgTextX, orgTextY; // used for rotation
29211
29212 var text = this.getLabelText(ele, prefix);
29213
29214 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29215 this.setupTextStyle(context, ele, useEleOpacity);
29216 var pdash = prefix ? prefix + '-' : '';
29217 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29218 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29219 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29220 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29221 var isEdge = ele.isEdge();
29222 var halign = ele.pstyle('text-halign').value;
29223 var valign = ele.pstyle('text-valign').value;
29224
29225 if (isEdge) {
29226 halign = 'center';
29227 valign = 'center';
29228 }
29229
29230 textX += marginX;
29231 textY += marginY;
29232 var theta;
29233
29234 if (!applyRotation) {
29235 theta = 0;
29236 } else {
29237 theta = this.getTextAngle(ele, prefix);
29238 }
29239
29240 if (theta !== 0) {
29241 orgTextX = textX;
29242 orgTextY = textY;
29243 context.translate(orgTextX, orgTextY);
29244 context.rotate(theta);
29245 textX = 0;
29246 textY = 0;
29247 }
29248
29249 switch (valign) {
29250 case 'top':
29251 break;
29252
29253 case 'center':
29254 textY += textH / 2;
29255 break;
29256
29257 case 'bottom':
29258 textY += textH;
29259 break;
29260 }
29261
29262 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29263 var borderOpacity = ele.pstyle('text-border-opacity').value;
29264 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29265 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29266
29267 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29268 var bgX = textX - backgroundPadding;
29269
29270 switch (halign) {
29271 case 'left':
29272 bgX -= textW;
29273 break;
29274
29275 case 'center':
29276 bgX -= textW / 2;
29277 break;
29278 }
29279
29280 var bgY = textY - textH - backgroundPadding;
29281 var bgW = textW + 2 * backgroundPadding;
29282 var bgH = textH + 2 * backgroundPadding;
29283
29284 if (backgroundOpacity > 0) {
29285 var textFill = context.fillStyle;
29286 var textBackgroundColor = ele.pstyle('text-background-color').value;
29287 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29288 var styleShape = ele.pstyle('text-background-shape').strValue;
29289
29290 if (styleShape.indexOf('round') === 0) {
29291 roundRect(context, bgX, bgY, bgW, bgH, 2);
29292 } else {
29293 context.fillRect(bgX, bgY, bgW, bgH);
29294 }
29295
29296 context.fillStyle = textFill;
29297 }
29298
29299 if (textBorderWidth > 0 && borderOpacity > 0) {
29300 var textStroke = context.strokeStyle;
29301 var textLineWidth = context.lineWidth;
29302 var textBorderColor = ele.pstyle('text-border-color').value;
29303 var textBorderStyle = ele.pstyle('text-border-style').value;
29304 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29305 context.lineWidth = textBorderWidth;
29306
29307 if (context.setLineDash) {
29308 // for very outofdate browsers
29309 switch (textBorderStyle) {
29310 case 'dotted':
29311 context.setLineDash([1, 1]);
29312 break;
29313
29314 case 'dashed':
29315 context.setLineDash([4, 2]);
29316 break;
29317
29318 case 'double':
29319 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29320
29321 context.setLineDash([]);
29322 break;
29323
29324 case 'solid':
29325 context.setLineDash([]);
29326 break;
29327 }
29328 }
29329
29330 context.strokeRect(bgX, bgY, bgW, bgH);
29331
29332 if (textBorderStyle === 'double') {
29333 var whiteWidth = textBorderWidth / 2;
29334 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29335 }
29336
29337 if (context.setLineDash) {
29338 // for very outofdate browsers
29339 context.setLineDash([]);
29340 }
29341
29342 context.lineWidth = textLineWidth;
29343 context.strokeStyle = textStroke;
29344 }
29345 }
29346
29347 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29348
29349 if (lineWidth > 0) {
29350 context.lineWidth = lineWidth;
29351 }
29352
29353 if (ele.pstyle('text-wrap').value === 'wrap') {
29354 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29355 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29356 var halfTextW = textW / 2;
29357 var justification = this.getLabelJustification(ele);
29358
29359 if (justification === 'auto') ; else if (halign === 'left') {
29360 // auto justification : right
29361 if (justification === 'left') {
29362 textX += -textW;
29363 } else if (justification === 'center') {
29364 textX += -halfTextW;
29365 } // else same as auto
29366
29367 } else if (halign === 'center') {
29368 // auto justfication : center
29369 if (justification === 'left') {
29370 textX += -halfTextW;
29371 } else if (justification === 'right') {
29372 textX += halfTextW;
29373 } // else same as auto
29374
29375 } else if (halign === 'right') {
29376 // auto justification : left
29377 if (justification === 'center') {
29378 textX += halfTextW;
29379 } else if (justification === 'right') {
29380 textX += textW;
29381 } // else same as auto
29382
29383 }
29384
29385 switch (valign) {
29386 case 'top':
29387 textY -= (lines.length - 1) * lineHeight;
29388 break;
29389
29390 case 'center':
29391 case 'bottom':
29392 textY -= (lines.length - 1) * lineHeight;
29393 break;
29394 }
29395
29396 for (var l = 0; l < lines.length; l++) {
29397 if (lineWidth > 0) {
29398 context.strokeText(lines[l], textX, textY);
29399 }
29400
29401 context.fillText(lines[l], textX, textY);
29402 textY += lineHeight;
29403 }
29404 } else {
29405 if (lineWidth > 0) {
29406 context.strokeText(text, textX, textY);
29407 }
29408
29409 context.fillText(text, textX, textY);
29410 }
29411
29412 if (theta !== 0) {
29413 context.rotate(-theta);
29414 context.translate(-orgTextX, -orgTextY);
29415 }
29416 }
29417};
29418
29419/* global Path2D */
29420var CRp$5 = {};
29421
29422CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29423 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29424 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29425 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29426 var r = this;
29427 var nodeWidth, nodeHeight;
29428 var _p = node._private;
29429 var rs = _p.rscratch;
29430 var pos = node.position();
29431
29432 if (!number(pos.x) || !number(pos.y)) {
29433 return; // can't draw node with undefined position
29434 }
29435
29436 if (shouldDrawOpacity && !node.visible()) {
29437 return;
29438 }
29439
29440 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29441 var usePaths = r.usePaths();
29442 var path;
29443 var pathCacheHit = false;
29444 var padding = node.padding();
29445 nodeWidth = node.width() + 2 * padding;
29446 nodeHeight = node.height() + 2 * padding; //
29447 // setup shift
29448
29449 var bb;
29450
29451 if (shiftToOriginWithBb) {
29452 bb = shiftToOriginWithBb;
29453 context.translate(-bb.x1, -bb.y1);
29454 } //
29455 // load bg image
29456
29457
29458 var bgImgProp = node.pstyle('background-image');
29459 var urls = bgImgProp.value;
29460 var urlDefined = new Array(urls.length);
29461 var image = new Array(urls.length);
29462 var numImages = 0;
29463
29464 for (var i = 0; i < urls.length; i++) {
29465 var url = urls[i];
29466 var defd = urlDefined[i] = url != null && url !== 'none';
29467
29468 if (defd) {
29469 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29470 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29471
29472 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29473 _p.backgroundTimestamp = Date.now();
29474 node.emitAndNotify('background');
29475 });
29476 }
29477 } //
29478 // setup styles
29479
29480
29481 var darkness = node.pstyle('background-blacken').value;
29482 var borderWidth = node.pstyle('border-width').pfValue;
29483 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29484 var borderColor = node.pstyle('border-color').value;
29485 var borderStyle = node.pstyle('border-style').value;
29486 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29487 context.lineJoin = 'miter'; // so borders are square with the node shape
29488
29489 var setupShapeColor = function setupShapeColor() {
29490 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29491 r.eleFillStyle(context, node, bgOpy);
29492 };
29493
29494 var setupBorderColor = function setupBorderColor() {
29495 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29496 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29497 }; //
29498 // setup shape
29499
29500
29501 var styleShape = node.pstyle('shape').strValue;
29502 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29503
29504 if (usePaths) {
29505 context.translate(pos.x, pos.y);
29506 var pathCache = r.nodePathCache = r.nodePathCache || [];
29507 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29508 var cachedPath = pathCache[key];
29509
29510 if (cachedPath != null) {
29511 path = cachedPath;
29512 pathCacheHit = true;
29513 rs.pathCache = path;
29514 } else {
29515 path = new Path2D();
29516 pathCache[key] = rs.pathCache = path;
29517 }
29518 }
29519
29520 var drawShape = function drawShape() {
29521 if (!pathCacheHit) {
29522 var npos = pos;
29523
29524 if (usePaths) {
29525 npos = {
29526 x: 0,
29527 y: 0
29528 };
29529 }
29530
29531 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29532 }
29533
29534 if (usePaths) {
29535 context.fill(path);
29536 } else {
29537 context.fill();
29538 }
29539 };
29540
29541 var drawImages = function drawImages() {
29542 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29543 var prevBging = _p.backgrounding;
29544 var totalCompleted = 0;
29545
29546 for (var _i = 0; _i < image.length; _i++) {
29547 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29548 totalCompleted++;
29549 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29550 }
29551 }
29552
29553 _p.backgrounding = !(totalCompleted === numImages);
29554
29555 if (prevBging !== _p.backgrounding) {
29556 // update style b/c :backgrounding state changed
29557 node.updateStyle(false);
29558 }
29559 };
29560
29561 var drawPie = function drawPie() {
29562 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29563 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29564
29565 if (r.hasPie(node)) {
29566 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29567
29568 if (redrawShape) {
29569 if (!usePaths) {
29570 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29571 }
29572 }
29573 }
29574 };
29575
29576 var darken = function darken() {
29577 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29578 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29579 var c = darkness > 0 ? 0 : 255;
29580
29581 if (darkness !== 0) {
29582 r.colorFillStyle(context, c, c, c, opacity);
29583
29584 if (usePaths) {
29585 context.fill(path);
29586 } else {
29587 context.fill();
29588 }
29589 }
29590 };
29591
29592 var drawBorder = function drawBorder() {
29593 if (borderWidth > 0) {
29594 context.lineWidth = borderWidth;
29595 context.lineCap = 'butt';
29596
29597 if (context.setLineDash) {
29598 // for very outofdate browsers
29599 switch (borderStyle) {
29600 case 'dotted':
29601 context.setLineDash([1, 1]);
29602 break;
29603
29604 case 'dashed':
29605 context.setLineDash([4, 2]);
29606 break;
29607
29608 case 'solid':
29609 case 'double':
29610 context.setLineDash([]);
29611 break;
29612 }
29613 }
29614
29615 if (usePaths) {
29616 context.stroke(path);
29617 } else {
29618 context.stroke();
29619 }
29620
29621 if (borderStyle === 'double') {
29622 context.lineWidth = borderWidth / 3;
29623 var gco = context.globalCompositeOperation;
29624 context.globalCompositeOperation = 'destination-out';
29625
29626 if (usePaths) {
29627 context.stroke(path);
29628 } else {
29629 context.stroke();
29630 }
29631
29632 context.globalCompositeOperation = gco;
29633 } // reset in case we changed the border style
29634
29635
29636 if (context.setLineDash) {
29637 // for very outofdate browsers
29638 context.setLineDash([]);
29639 }
29640 }
29641 };
29642
29643 var drawOverlay = function drawOverlay() {
29644 if (shouldDrawOverlay) {
29645 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
29646 }
29647 };
29648
29649 var drawText = function drawText() {
29650 r.drawElementText(context, node, null, drawLabel);
29651 };
29652
29653 var ghost = node.pstyle('ghost').value === 'yes';
29654
29655 if (ghost) {
29656 var gx = node.pstyle('ghost-offset-x').pfValue;
29657 var gy = node.pstyle('ghost-offset-y').pfValue;
29658 var ghostOpacity = node.pstyle('ghost-opacity').value;
29659 var effGhostOpacity = ghostOpacity * eleOpacity;
29660 context.translate(gx, gy);
29661 setupShapeColor(ghostOpacity * bgOpacity);
29662 drawShape();
29663 drawImages(effGhostOpacity);
29664 drawPie(darkness !== 0 || borderWidth !== 0);
29665 darken(effGhostOpacity);
29666 setupBorderColor(ghostOpacity * borderOpacity);
29667 drawBorder();
29668 context.translate(-gx, -gy);
29669 }
29670
29671 setupShapeColor();
29672 drawShape();
29673 drawImages();
29674 drawPie(darkness !== 0 || borderWidth !== 0);
29675 darken();
29676 setupBorderColor();
29677 drawBorder();
29678
29679 if (usePaths) {
29680 context.translate(-pos.x, -pos.y);
29681 }
29682
29683 drawText();
29684 drawOverlay(); //
29685 // clean up shift
29686
29687 if (shiftToOriginWithBb) {
29688 context.translate(bb.x1, bb.y1);
29689 }
29690};
29691
29692CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
29693 var r = this;
29694
29695 if (!node.visible()) {
29696 return;
29697 }
29698
29699 var overlayPadding = node.pstyle('overlay-padding').pfValue;
29700 var overlayOpacity = node.pstyle('overlay-opacity').value;
29701 var overlayColor = node.pstyle('overlay-color').value;
29702
29703 if (overlayOpacity > 0) {
29704 pos = pos || node.position();
29705
29706 if (nodeWidth == null || nodeHeight == null) {
29707 var padding = node.padding();
29708 nodeWidth = node.width() + 2 * padding;
29709 nodeHeight = node.height() + 2 * padding;
29710 }
29711
29712 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29713 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
29714 context.fill();
29715 }
29716}; // does the node have at least one pie piece?
29717
29718
29719CRp$5.hasPie = function (node) {
29720 node = node[0]; // ensure ele ref
29721
29722 return node._private.hasPie;
29723};
29724
29725CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
29726 node = node[0]; // ensure ele ref
29727
29728 pos = pos || node.position();
29729 var cyStyle = node.cy().style();
29730 var pieSize = node.pstyle('pie-size');
29731 var x = pos.x;
29732 var y = pos.y;
29733 var nodeW = node.width();
29734 var nodeH = node.height();
29735 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
29736
29737 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
29738
29739 var usePaths = this.usePaths();
29740
29741 if (usePaths) {
29742 x = 0;
29743 y = 0;
29744 }
29745
29746 if (pieSize.units === '%') {
29747 radius = radius * pieSize.pfValue;
29748 } else if (pieSize.pfValue !== undefined) {
29749 radius = pieSize.pfValue / 2;
29750 }
29751
29752 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
29753 // 1..N
29754 var size = node.pstyle('pie-' + i + '-background-size').value;
29755 var color = node.pstyle('pie-' + i + '-background-color').value;
29756 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
29757 var percent = size / 100; // map integer range [0, 100] to [0, 1]
29758 // percent can't push beyond 1
29759
29760 if (percent + lastPercent > 1) {
29761 percent = 1 - lastPercent;
29762 }
29763
29764 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
29765
29766 var angleDelta = 2 * Math.PI * percent;
29767 var angleEnd = angleStart + angleDelta; // ignore if
29768 // - zero size
29769 // - we're already beyond the full circle
29770 // - adding the current slice would go beyond the full circle
29771
29772 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
29773 continue;
29774 }
29775
29776 context.beginPath();
29777 context.moveTo(x, y);
29778 context.arc(x, y, radius, angleStart, angleEnd);
29779 context.closePath();
29780 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29781 context.fill();
29782 lastPercent += percent;
29783 }
29784};
29785
29786var CRp$6 = {};
29787var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
29788
29789CRp$6.getPixelRatio = function () {
29790 var context = this.data.contexts[0];
29791
29792 if (this.forcedPixelRatio != null) {
29793 return this.forcedPixelRatio;
29794 }
29795
29796 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
29797 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
29798};
29799
29800CRp$6.paintCache = function (context) {
29801 var caches = this.paintCaches = this.paintCaches || [];
29802 var needToCreateCache = true;
29803 var cache;
29804
29805 for (var i = 0; i < caches.length; i++) {
29806 cache = caches[i];
29807
29808 if (cache.context === context) {
29809 needToCreateCache = false;
29810 break;
29811 }
29812 }
29813
29814 if (needToCreateCache) {
29815 cache = {
29816 context: context
29817 };
29818 caches.push(cache);
29819 }
29820
29821 return cache;
29822};
29823
29824CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
29825 var gradientStyle;
29826 var usePaths = this.usePaths();
29827 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
29828 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
29829
29830 if (fill === 'radial-gradient') {
29831 if (ele.isEdge()) {
29832 var start = ele.sourceEndpoint(),
29833 end = ele.targetEndpoint(),
29834 mid = ele.midpoint();
29835 var d1 = dist(start, mid);
29836 var d2 = dist(end, mid);
29837 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
29838 } else {
29839 var pos = usePaths ? {
29840 x: 0,
29841 y: 0
29842 } : ele.position(),
29843 width = ele.paddedWidth(),
29844 height = ele.paddedHeight();
29845 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
29846 }
29847 } else {
29848 if (ele.isEdge()) {
29849 var _start = ele.sourceEndpoint(),
29850 _end = ele.targetEndpoint();
29851
29852 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
29853 } else {
29854 var _pos = usePaths ? {
29855 x: 0,
29856 y: 0
29857 } : ele.position(),
29858 _width = ele.paddedWidth(),
29859 _height = ele.paddedHeight(),
29860 halfWidth = _width / 2,
29861 halfHeight = _height / 2;
29862
29863 var direction = ele.pstyle('background-gradient-direction').value;
29864
29865 switch (direction) {
29866 case 'to-bottom':
29867 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
29868 break;
29869
29870 case 'to-top':
29871 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
29872 break;
29873
29874 case 'to-left':
29875 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
29876 break;
29877
29878 case 'to-right':
29879 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
29880 break;
29881
29882 case 'to-bottom-right':
29883 case 'to-right-bottom':
29884 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
29885 break;
29886
29887 case 'to-top-right':
29888 case 'to-right-top':
29889 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
29890 break;
29891
29892 case 'to-bottom-left':
29893 case 'to-left-bottom':
29894 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
29895 break;
29896
29897 case 'to-top-left':
29898 case 'to-left-top':
29899 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
29900 break;
29901 }
29902 }
29903 }
29904
29905 if (!gradientStyle) return null; // invalid gradient style
29906
29907 var hasPositions = positions.length === colors.length;
29908 var length = colors.length;
29909
29910 for (var i = 0; i < length; i++) {
29911 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
29912 }
29913
29914 return gradientStyle;
29915};
29916
29917CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
29918 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
29919 if (!gradientStyle) return null; // error
29920
29921 context.fillStyle = gradientStyle;
29922};
29923
29924CRp$6.colorFillStyle = function (context, r, g, b, a) {
29925 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
29926 // var cache = this.paintCache(context);
29927 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29928 // if( cache.fillStyle !== fillStyle ){
29929 // context.fillStyle = cache.fillStyle = fillStyle;
29930 // }
29931};
29932
29933CRp$6.eleFillStyle = function (context, ele, opacity) {
29934 var backgroundFill = ele.pstyle('background-fill').value;
29935
29936 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
29937 this.gradientFillStyle(context, ele, backgroundFill, opacity);
29938 } else {
29939 var backgroundColor = ele.pstyle('background-color').value;
29940 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
29941 }
29942};
29943
29944CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
29945 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
29946 if (!gradientStyle) return null; // error
29947
29948 context.strokeStyle = gradientStyle;
29949};
29950
29951CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
29952 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
29953 // var cache = this.paintCache(context);
29954 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
29955 // if( cache.strokeStyle !== strokeStyle ){
29956 // context.strokeStyle = cache.strokeStyle = strokeStyle;
29957 // }
29958};
29959
29960CRp$6.eleStrokeStyle = function (context, ele, opacity) {
29961 var lineFill = ele.pstyle('line-fill').value;
29962
29963 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
29964 this.gradientStrokeStyle(context, ele, lineFill, opacity);
29965 } else {
29966 var lineColor = ele.pstyle('line-color').value;
29967 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
29968 }
29969}; // Resize canvas
29970
29971
29972CRp$6.matchCanvasSize = function (container) {
29973 var r = this;
29974 var data = r.data;
29975 var bb = r.findContainerClientCoords();
29976 var width = bb[2];
29977 var height = bb[3];
29978 var pixelRatio = r.getPixelRatio();
29979 var mbPxRatio = r.motionBlurPxRatio;
29980
29981 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
29982 pixelRatio = mbPxRatio;
29983 }
29984
29985 var canvasWidth = width * pixelRatio;
29986 var canvasHeight = height * pixelRatio;
29987 var canvas;
29988
29989 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
29990 return; // save cycles if same
29991 }
29992
29993 r.fontCaches = null; // resizing resets the style
29994
29995 var canvasContainer = data.canvasContainer;
29996 canvasContainer.style.width = width + 'px';
29997 canvasContainer.style.height = height + 'px';
29998
29999 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30000 canvas = data.canvases[i];
30001 canvas.width = canvasWidth;
30002 canvas.height = canvasHeight;
30003 canvas.style.width = width + 'px';
30004 canvas.style.height = height + 'px';
30005 }
30006
30007 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30008 canvas = data.bufferCanvases[i];
30009 canvas.width = canvasWidth;
30010 canvas.height = canvasHeight;
30011 canvas.style.width = width + 'px';
30012 canvas.style.height = height + 'px';
30013 }
30014
30015 r.textureMult = 1;
30016
30017 if (pixelRatio <= 1) {
30018 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30019 r.textureMult = 2;
30020 canvas.width = canvasWidth * r.textureMult;
30021 canvas.height = canvasHeight * r.textureMult;
30022 }
30023
30024 r.canvasWidth = canvasWidth;
30025 r.canvasHeight = canvasHeight;
30026};
30027
30028CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30029 this.render({
30030 forcedContext: cxt,
30031 forcedZoom: zoom,
30032 forcedPan: pan,
30033 drawAllLayers: true,
30034 forcedPxRatio: pxRatio
30035 });
30036};
30037
30038CRp$6.render = function (options) {
30039 options = options || staticEmptyObject();
30040 var forcedContext = options.forcedContext;
30041 var drawAllLayers = options.drawAllLayers;
30042 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30043 var forcedZoom = options.forcedZoom;
30044 var forcedPan = options.forcedPan;
30045 var r = this;
30046 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30047 var cy = r.cy;
30048 var data = r.data;
30049 var needDraw = data.canvasNeedsRedraw;
30050 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30051 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30052 var mbPxRatio = r.motionBlurPxRatio;
30053 var hasCompoundNodes = cy.hasCompoundNodes();
30054 var inNodeDragGesture = r.hoverData.draggingEles;
30055 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30056 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30057 var motionBlurFadeEffect = motionBlur;
30058
30059 if (!forcedContext) {
30060 if (r.prevPxRatio !== pixelRatio) {
30061 r.invalidateContainerClientCoordsCache();
30062 r.matchCanvasSize(r.container);
30063 r.redrawHint('eles', true);
30064 r.redrawHint('drag', true);
30065 }
30066
30067 r.prevPxRatio = pixelRatio;
30068 }
30069
30070 if (!forcedContext && r.motionBlurTimeout) {
30071 clearTimeout(r.motionBlurTimeout);
30072 }
30073
30074 if (motionBlur) {
30075 if (r.mbFrames == null) {
30076 r.mbFrames = 0;
30077 }
30078
30079 r.mbFrames++;
30080
30081 if (r.mbFrames < 3) {
30082 // need several frames before even high quality motionblur
30083 motionBlurFadeEffect = false;
30084 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30085
30086
30087 if (r.mbFrames > r.minMbLowQualFrames) {
30088 //r.fullQualityMb = false;
30089 r.motionBlurPxRatio = r.mbPxRBlurry;
30090 }
30091 }
30092
30093 if (r.clearingMotionBlur) {
30094 r.motionBlurPxRatio = 1;
30095 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30096 // because a rogue async texture frame would clear needDraw
30097
30098
30099 if (r.textureDrawLastFrame && !textureDraw) {
30100 needDraw[r.NODE] = true;
30101 needDraw[r.SELECT_BOX] = true;
30102 }
30103
30104 var style = cy.style();
30105 var zoom = cy.zoom();
30106 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30107 var pan = cy.pan();
30108 var effectivePan = {
30109 x: pan.x,
30110 y: pan.y
30111 };
30112 var vp = {
30113 zoom: zoom,
30114 pan: {
30115 x: pan.x,
30116 y: pan.y
30117 }
30118 };
30119 var prevVp = r.prevViewport;
30120 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)
30121
30122 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30123 r.motionBlurPxRatio = 1;
30124 }
30125
30126 if (forcedPan) {
30127 effectivePan = forcedPan;
30128 } // apply pixel ratio
30129
30130
30131 effectiveZoom *= pixelRatio;
30132 effectivePan.x *= pixelRatio;
30133 effectivePan.y *= pixelRatio;
30134 var eles = r.getCachedZSortedEles();
30135
30136 function mbclear(context, x, y, w, h) {
30137 var gco = context.globalCompositeOperation;
30138 context.globalCompositeOperation = 'destination-out';
30139 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30140 context.fillRect(x, y, w, h);
30141 context.globalCompositeOperation = gco;
30142 }
30143
30144 function setContextTransform(context, clear) {
30145 var ePan, eZoom, w, h;
30146
30147 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30148 ePan = {
30149 x: pan.x * mbPxRatio,
30150 y: pan.y * mbPxRatio
30151 };
30152 eZoom = zoom * mbPxRatio;
30153 w = r.canvasWidth * mbPxRatio;
30154 h = r.canvasHeight * mbPxRatio;
30155 } else {
30156 ePan = effectivePan;
30157 eZoom = effectiveZoom;
30158 w = r.canvasWidth;
30159 h = r.canvasHeight;
30160 }
30161
30162 context.setTransform(1, 0, 0, 1, 0, 0);
30163
30164 if (clear === 'motionBlur') {
30165 mbclear(context, 0, 0, w, h);
30166 } else if (!forcedContext && (clear === undefined || clear)) {
30167 context.clearRect(0, 0, w, h);
30168 }
30169
30170 if (!drawAllLayers) {
30171 context.translate(ePan.x, ePan.y);
30172 context.scale(eZoom, eZoom);
30173 }
30174
30175 if (forcedPan) {
30176 context.translate(forcedPan.x, forcedPan.y);
30177 }
30178
30179 if (forcedZoom) {
30180 context.scale(forcedZoom, forcedZoom);
30181 }
30182 }
30183
30184 if (!textureDraw) {
30185 r.textureDrawLastFrame = false;
30186 }
30187
30188 if (textureDraw) {
30189 r.textureDrawLastFrame = true;
30190
30191 if (!r.textureCache) {
30192 r.textureCache = {};
30193 r.textureCache.bb = cy.mutableElements().boundingBox();
30194 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30195 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30196 cxt.setTransform(1, 0, 0, 1, 0, 0);
30197 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30198 r.render({
30199 forcedContext: cxt,
30200 drawOnlyNodeLayer: true,
30201 forcedPxRatio: pixelRatio * r.textureMult
30202 });
30203 var vp = r.textureCache.viewport = {
30204 zoom: cy.zoom(),
30205 pan: cy.pan(),
30206 width: r.canvasWidth,
30207 height: r.canvasHeight
30208 };
30209 vp.mpan = {
30210 x: (0 - vp.pan.x) / vp.zoom,
30211 y: (0 - vp.pan.y) / vp.zoom
30212 };
30213 }
30214
30215 needDraw[r.DRAG] = false;
30216 needDraw[r.NODE] = false;
30217 var context = data.contexts[r.NODE];
30218 var texture = r.textureCache.texture;
30219 var vp = r.textureCache.viewport;
30220 context.setTransform(1, 0, 0, 1, 0, 0);
30221
30222 if (motionBlur) {
30223 mbclear(context, 0, 0, vp.width, vp.height);
30224 } else {
30225 context.clearRect(0, 0, vp.width, vp.height);
30226 }
30227
30228 var outsideBgColor = style.core('outside-texture-bg-color').value;
30229 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30230 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30231 context.fillRect(0, 0, vp.width, vp.height);
30232 var zoom = cy.zoom();
30233 setContextTransform(context, false);
30234 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30235 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30236 } else if (r.textureOnViewport && !forcedContext) {
30237 // clear the cache since we don't need it
30238 r.textureCache = null;
30239 }
30240
30241 var extent = cy.extent();
30242 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30243 var hideEdges = r.hideEdgesOnViewport && vpManip;
30244 var needMbClear = [];
30245 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30246
30247 if (needMbClear[r.NODE]) {
30248 r.clearedForMotionBlur[r.NODE] = true;
30249 }
30250
30251 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30252
30253 if (needMbClear[r.DRAG]) {
30254 r.clearedForMotionBlur[r.DRAG] = true;
30255 }
30256
30257 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30258 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30259 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30260 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30261 setContextTransform(context, clear);
30262
30263 if (hideEdges) {
30264 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30265 } else {
30266 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30267 }
30268
30269 if (r.debug) {
30270 r.drawDebugPoints(context, eles.nondrag);
30271 }
30272
30273 if (!drawAllLayers && !motionBlur) {
30274 needDraw[r.NODE] = false;
30275 }
30276 }
30277
30278 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30279 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30280 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30281 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30282
30283 if (hideEdges) {
30284 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30285 } else {
30286 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30287 }
30288
30289 if (r.debug) {
30290 r.drawDebugPoints(context, eles.drag);
30291 }
30292
30293 if (!drawAllLayers && !motionBlur) {
30294 needDraw[r.DRAG] = false;
30295 }
30296 }
30297
30298 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30299 var context = forcedContext || data.contexts[r.SELECT_BOX];
30300 setContextTransform(context);
30301
30302 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30303 var zoom = r.cy.zoom();
30304 var borderWidth = style.core('selection-box-border-width').value / zoom;
30305 context.lineWidth = borderWidth;
30306 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 + ')';
30307 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30308
30309 if (borderWidth > 0) {
30310 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 + ')';
30311 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30312 }
30313 }
30314
30315 if (data.bgActivePosistion && !r.hoverData.selecting) {
30316 var zoom = r.cy.zoom();
30317 var pos = data.bgActivePosistion;
30318 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 + ')';
30319 context.beginPath();
30320 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30321 context.fill();
30322 }
30323
30324 var timeToRender = r.lastRedrawTime;
30325
30326 if (r.showFps && timeToRender) {
30327 timeToRender = Math.round(timeToRender);
30328 var fps = Math.round(1000 / timeToRender);
30329 context.setTransform(1, 0, 0, 1, 0, 0);
30330 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30331 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30332 context.lineWidth = 1;
30333 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30334 var maxFps = 60;
30335 context.strokeRect(0, 30, 250, 20);
30336 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30337 }
30338
30339 if (!drawAllLayers) {
30340 needDraw[r.SELECT_BOX] = false;
30341 }
30342 } // motionblur: blit rendered blurry frames
30343
30344
30345 if (motionBlur && mbPxRatio !== 1) {
30346 var cxtNode = data.contexts[r.NODE];
30347 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30348 var cxtDrag = data.contexts[r.DRAG];
30349 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30350
30351 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30352 cxt.setTransform(1, 0, 0, 1, 0, 0);
30353
30354 if (needClear || !motionBlurFadeEffect) {
30355 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30356 } else {
30357 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30358 }
30359
30360 var pxr = mbPxRatio;
30361 cxt.drawImage(txt, // img
30362 0, 0, // sx, sy
30363 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30364 0, 0, // x, y
30365 r.canvasWidth, r.canvasHeight // w, h
30366 );
30367 };
30368
30369 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30370 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30371 needDraw[r.NODE] = false;
30372 }
30373
30374 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30375 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30376 needDraw[r.DRAG] = false;
30377 }
30378 }
30379
30380 r.prevViewport = vp;
30381
30382 if (r.clearingMotionBlur) {
30383 r.clearingMotionBlur = false;
30384 r.motionBlurCleared = true;
30385 r.motionBlur = true;
30386 }
30387
30388 if (motionBlur) {
30389 r.motionBlurTimeout = setTimeout(function () {
30390 r.motionBlurTimeout = null;
30391 r.clearedForMotionBlur[r.NODE] = false;
30392 r.clearedForMotionBlur[r.DRAG] = false;
30393 r.motionBlur = false;
30394 r.clearingMotionBlur = !textureDraw;
30395 r.mbFrames = 0;
30396 needDraw[r.NODE] = true;
30397 needDraw[r.DRAG] = true;
30398 r.redraw();
30399 }, motionBlurDelay);
30400 }
30401
30402 if (!forcedContext) {
30403 cy.emit('render');
30404 }
30405};
30406
30407var CRp$7 = {}; // @O Polygon drawing
30408
30409CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
30410 var halfW = width / 2;
30411 var halfH = height / 2;
30412
30413 if (context.beginPath) {
30414 context.beginPath();
30415 }
30416
30417 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30418
30419 for (var i = 1; i < points.length / 2; i++) {
30420 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30421 }
30422
30423 context.closePath();
30424};
30425
30426CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30427 var halfW = width / 2;
30428 var halfH = height / 2;
30429 var cornerRadius = getRoundPolygonRadius(width, height);
30430
30431 if (context.beginPath) {
30432 context.beginPath();
30433 }
30434
30435 for (var _i = 0; _i < points.length / 4; _i++) {
30436 var sourceUv = void 0,
30437 destUv = void 0;
30438
30439 if (_i === 0) {
30440 sourceUv = points.length - 2;
30441 } else {
30442 sourceUv = _i * 4 - 2;
30443 }
30444
30445 destUv = _i * 4 + 2;
30446 var px = x + halfW * points[_i * 4];
30447 var py = y + halfH * points[_i * 4 + 1];
30448 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30449 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30450 var cp0x = px - offset * points[sourceUv];
30451 var cp0y = py - offset * points[sourceUv + 1];
30452 var cp1x = px + offset * points[destUv];
30453 var cp1y = py + offset * points[destUv + 1];
30454
30455 if (_i === 0) {
30456 context.moveTo(cp0x, cp0y);
30457 } else {
30458 context.lineTo(cp0x, cp0y);
30459 }
30460
30461 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30462 }
30463
30464 context.closePath();
30465}; // Round rectangle drawing
30466
30467
30468CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
30469 var halfWidth = width / 2;
30470 var halfHeight = height / 2;
30471 var cornerRadius = getRoundRectangleRadius(width, height);
30472
30473 if (context.beginPath) {
30474 context.beginPath();
30475 } // Start at top middle
30476
30477
30478 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30479
30480 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30481
30482 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30483
30484 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30485
30486 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30487
30488 context.lineTo(x, y - halfHeight);
30489 context.closePath();
30490};
30491
30492CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30493 var halfWidth = width / 2;
30494 var halfHeight = height / 2;
30495 var cornerRadius = getRoundRectangleRadius(width, height);
30496
30497 if (context.beginPath) {
30498 context.beginPath();
30499 } // Start at top middle
30500
30501
30502 context.moveTo(x, y - halfHeight);
30503 context.lineTo(x + halfWidth, y - halfHeight);
30504 context.lineTo(x + halfWidth, y);
30505 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30506 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30507 context.lineTo(x - halfWidth, y - halfHeight);
30508 context.lineTo(x, y - halfHeight);
30509 context.closePath();
30510};
30511
30512CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
30513 var halfWidth = width / 2;
30514 var halfHeight = height / 2;
30515 var cornerLength = getCutRectangleCornerLength();
30516
30517 if (context.beginPath) {
30518 context.beginPath();
30519 }
30520
30521 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30522 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30523 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30524 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30525 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30526 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30527 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30528 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30529 context.closePath();
30530};
30531
30532CRp$7.drawBarrelPath = function (context, x, y, width, height) {
30533 var halfWidth = width / 2;
30534 var halfHeight = height / 2;
30535 var xBegin = x - halfWidth;
30536 var xEnd = x + halfWidth;
30537 var yBegin = y - halfHeight;
30538 var yEnd = y + halfHeight;
30539 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30540 var wOffset = barrelCurveConstants.widthOffset;
30541 var hOffset = barrelCurveConstants.heightOffset;
30542 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30543
30544 if (context.beginPath) {
30545 context.beginPath();
30546 }
30547
30548 context.moveTo(xBegin, yBegin + hOffset);
30549 context.lineTo(xBegin, yEnd - hOffset);
30550 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30551 context.lineTo(xEnd - wOffset, yEnd);
30552 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30553 context.lineTo(xEnd, yBegin + hOffset);
30554 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30555 context.lineTo(xBegin + wOffset, yBegin);
30556 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30557 context.closePath();
30558};
30559
30560var sin0 = Math.sin(0);
30561var cos0 = Math.cos(0);
30562var sin = {};
30563var cos = {};
30564var ellipseStepSize = Math.PI / 40;
30565
30566for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30567 sin[i] = Math.sin(i);
30568 cos[i] = Math.cos(i);
30569}
30570
30571CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
30572 if (context.beginPath) {
30573 context.beginPath();
30574 }
30575
30576 if (context.ellipse) {
30577 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30578 } else {
30579 var xPos, yPos;
30580 var rw = width / 2;
30581 var rh = height / 2;
30582
30583 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30584 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30585 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30586
30587 if (i === 0) {
30588 context.moveTo(xPos, yPos);
30589 } else {
30590 context.lineTo(xPos, yPos);
30591 }
30592 }
30593 }
30594
30595 context.closePath();
30596};
30597
30598/* global atob, ArrayBuffer, Uint8Array, Blob */
30599var CRp$8 = {};
30600
30601CRp$8.createBuffer = function (w, h) {
30602 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30603
30604 buffer.width = w;
30605 buffer.height = h;
30606 return [buffer, buffer.getContext('2d')];
30607};
30608
30609CRp$8.bufferCanvasImage = function (options) {
30610 var cy = this.cy;
30611 var eles = cy.mutableElements();
30612 var bb = eles.boundingBox();
30613 var ctrRect = this.findContainerClientCoords();
30614 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
30615 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
30616 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
30617 var pxRatio = this.getPixelRatio();
30618 var scale = 1;
30619
30620 if (options.scale !== undefined) {
30621 width *= options.scale;
30622 height *= options.scale;
30623 scale = options.scale;
30624 } else if (specdMaxDims) {
30625 var maxScaleW = Infinity;
30626 var maxScaleH = Infinity;
30627
30628 if (number(options.maxWidth)) {
30629 maxScaleW = scale * options.maxWidth / width;
30630 }
30631
30632 if (number(options.maxHeight)) {
30633 maxScaleH = scale * options.maxHeight / height;
30634 }
30635
30636 scale = Math.min(maxScaleW, maxScaleH);
30637 width *= scale;
30638 height *= scale;
30639 }
30640
30641 if (!specdMaxDims) {
30642 width *= pxRatio;
30643 height *= pxRatio;
30644 scale *= pxRatio;
30645 }
30646
30647 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
30648
30649 buffCanvas.width = width;
30650 buffCanvas.height = height;
30651 buffCanvas.style.width = width + 'px';
30652 buffCanvas.style.height = height + 'px';
30653 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
30654
30655 if (width > 0 && height > 0) {
30656 buffCxt.clearRect(0, 0, width, height);
30657 buffCxt.globalCompositeOperation = 'source-over';
30658 var zsortedEles = this.getCachedZSortedEles();
30659
30660 if (options.full) {
30661 // draw the full bounds of the graph
30662 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
30663 buffCxt.scale(scale, scale);
30664 this.drawElements(buffCxt, zsortedEles);
30665 buffCxt.scale(1 / scale, 1 / scale);
30666 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
30667 } else {
30668 // draw the current view
30669 var pan = cy.pan();
30670 var translation = {
30671 x: pan.x * scale,
30672 y: pan.y * scale
30673 };
30674 scale *= cy.zoom();
30675 buffCxt.translate(translation.x, translation.y);
30676 buffCxt.scale(scale, scale);
30677 this.drawElements(buffCxt, zsortedEles);
30678 buffCxt.scale(1 / scale, 1 / scale);
30679 buffCxt.translate(-translation.x, -translation.y);
30680 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
30681
30682
30683 if (options.bg) {
30684 buffCxt.globalCompositeOperation = 'destination-over';
30685 buffCxt.fillStyle = options.bg;
30686 buffCxt.rect(0, 0, width, height);
30687 buffCxt.fill();
30688 }
30689 }
30690
30691 return buffCanvas;
30692};
30693
30694function b64ToBlob(b64, mimeType) {
30695 var bytes = atob(b64);
30696 var buff = new ArrayBuffer(bytes.length);
30697 var buffUint8 = new Uint8Array(buff);
30698
30699 for (var i = 0; i < bytes.length; i++) {
30700 buffUint8[i] = bytes.charCodeAt(i);
30701 }
30702
30703 return new Blob([buff], {
30704 type: mimeType
30705 });
30706}
30707
30708function b64UriToB64(b64uri) {
30709 var i = b64uri.indexOf(',');
30710 return b64uri.substr(i + 1);
30711}
30712
30713function output(options, canvas, mimeType) {
30714 var getB64Uri = function getB64Uri() {
30715 return canvas.toDataURL(mimeType, options.quality);
30716 };
30717
30718 switch (options.output) {
30719 case 'blob-promise':
30720 return new Promise$1(function (resolve, reject) {
30721 try {
30722 canvas.toBlob(function (blob) {
30723 if (blob != null) {
30724 resolve(blob);
30725 } else {
30726 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
30727 }
30728 }, mimeType, options.quality);
30729 } catch (err) {
30730 reject(err);
30731 }
30732 });
30733
30734 case 'blob':
30735 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
30736
30737 case 'base64':
30738 return b64UriToB64(getB64Uri());
30739
30740 case 'base64uri':
30741 default:
30742 return getB64Uri();
30743 }
30744}
30745
30746CRp$8.png = function (options) {
30747 return output(options, this.bufferCanvasImage(options), 'image/png');
30748};
30749
30750CRp$8.jpg = function (options) {
30751 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
30752};
30753
30754var CRp$9 = {};
30755
30756CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
30757 switch (name) {
30758 case 'ellipse':
30759 return this.drawEllipsePath(context, centerX, centerY, width, height);
30760
30761 case 'polygon':
30762 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
30763
30764 case 'round-polygon':
30765 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
30766
30767 case 'roundrectangle':
30768 case 'round-rectangle':
30769 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
30770
30771 case 'cutrectangle':
30772 case 'cut-rectangle':
30773 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
30774
30775 case 'bottomroundrectangle':
30776 case 'bottom-round-rectangle':
30777 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
30778
30779 case 'barrel':
30780 return this.drawBarrelPath(context, centerX, centerY, width, height);
30781 }
30782};
30783
30784var CR = CanvasRenderer;
30785var CRp$a = CanvasRenderer.prototype;
30786CRp$a.CANVAS_LAYERS = 3; //
30787
30788CRp$a.SELECT_BOX = 0;
30789CRp$a.DRAG = 1;
30790CRp$a.NODE = 2;
30791CRp$a.BUFFER_COUNT = 3; //
30792
30793CRp$a.TEXTURE_BUFFER = 0;
30794CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
30795CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
30796
30797function CanvasRenderer(options) {
30798 var r = this;
30799 r.data = {
30800 canvases: new Array(CRp$a.CANVAS_LAYERS),
30801 contexts: new Array(CRp$a.CANVAS_LAYERS),
30802 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
30803 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
30804 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
30805 };
30806 var tapHlOffAttr = '-webkit-tap-highlight-color';
30807 var tapHlOffStyle = 'rgba(0,0,0,0)';
30808 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
30809
30810 var containerStyle = r.data.canvasContainer.style;
30811 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
30812 containerStyle.position = 'relative';
30813 containerStyle.zIndex = '0';
30814 containerStyle.overflow = 'hidden';
30815 var container = options.cy.container();
30816 container.appendChild(r.data.canvasContainer);
30817 container.style[tapHlOffAttr] = tapHlOffStyle;
30818 var styleMap = {
30819 '-webkit-user-select': 'none',
30820 '-moz-user-select': '-moz-none',
30821 'user-select': 'none',
30822 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
30823 'outline-style': 'none'
30824 };
30825
30826 if (ms()) {
30827 styleMap['-ms-touch-action'] = 'none';
30828 styleMap['touch-action'] = 'none';
30829 }
30830
30831 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
30832 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30833
30834 r.data.contexts[i] = canvas.getContext('2d');
30835 Object.keys(styleMap).forEach(function (k) {
30836 canvas.style[k] = styleMap[k];
30837 });
30838 canvas.style.position = 'absolute';
30839 canvas.setAttribute('data-id', 'layer' + i);
30840 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
30841 r.data.canvasContainer.appendChild(canvas);
30842 r.data.canvasNeedsRedraw[i] = false;
30843 }
30844
30845 r.data.topCanvas = r.data.canvases[0];
30846 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
30847 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
30848 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
30849
30850 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
30851 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30852
30853 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
30854 r.data.bufferCanvases[i].style.position = 'absolute';
30855 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
30856 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
30857 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
30858 }
30859
30860 r.pathsEnabled = true;
30861 var emptyBb = makeBoundingBox();
30862
30863 var getBoxCenter = function getBoxCenter(bb) {
30864 return {
30865 x: (bb.x1 + bb.x2) / 2,
30866 y: (bb.y1 + bb.y2) / 2
30867 };
30868 };
30869
30870 var getCenterOffset = function getCenterOffset(bb) {
30871 return {
30872 x: -bb.w / 2,
30873 y: -bb.h / 2
30874 };
30875 };
30876
30877 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
30878 var _p = ele[0]._private;
30879 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
30880 return !same;
30881 };
30882
30883 var getStyleKey = function getStyleKey(ele) {
30884 return ele[0]._private.nodeKey;
30885 };
30886
30887 var getLabelKey = function getLabelKey(ele) {
30888 return ele[0]._private.labelStyleKey;
30889 };
30890
30891 var getSourceLabelKey = function getSourceLabelKey(ele) {
30892 return ele[0]._private.sourceLabelStyleKey;
30893 };
30894
30895 var getTargetLabelKey = function getTargetLabelKey(ele) {
30896 return ele[0]._private.targetLabelStyleKey;
30897 };
30898
30899 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
30900 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
30901 };
30902
30903 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30904 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
30905 };
30906
30907 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30908 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
30909 };
30910
30911 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
30912 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
30913 };
30914
30915 var getElementBox = function getElementBox(ele) {
30916 ele.boundingBox();
30917 return ele[0]._private.bodyBounds;
30918 };
30919
30920 var getLabelBox = function getLabelBox(ele) {
30921 ele.boundingBox();
30922 return ele[0]._private.labelBounds.main || emptyBb;
30923 };
30924
30925 var getSourceLabelBox = function getSourceLabelBox(ele) {
30926 ele.boundingBox();
30927 return ele[0]._private.labelBounds.source || emptyBb;
30928 };
30929
30930 var getTargetLabelBox = function getTargetLabelBox(ele) {
30931 ele.boundingBox();
30932 return ele[0]._private.labelBounds.target || emptyBb;
30933 };
30934
30935 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
30936 return scaledLabelShown;
30937 };
30938
30939 var getElementRotationPoint = function getElementRotationPoint(ele) {
30940 return getBoxCenter(getElementBox(ele));
30941 };
30942
30943 var addTextMargin = function addTextMargin(prefix, pt, ele) {
30944 var pre = prefix ? prefix + '-' : '';
30945 return {
30946 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
30947 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
30948 };
30949 };
30950
30951 var getRsPt = function getRsPt(ele, x, y) {
30952 var rs = ele[0]._private.rscratch;
30953 return {
30954 x: rs[x],
30955 y: rs[y]
30956 };
30957 };
30958
30959 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
30960 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
30961 };
30962
30963 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
30964 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
30965 };
30966
30967 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
30968 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
30969 };
30970
30971 var getElementRotationOffset = function getElementRotationOffset(ele) {
30972 return getCenterOffset(getElementBox(ele));
30973 };
30974
30975 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
30976 return getCenterOffset(getSourceLabelBox(ele));
30977 };
30978
30979 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
30980 return getCenterOffset(getTargetLabelBox(ele));
30981 };
30982
30983 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
30984 var bb = getLabelBox(ele);
30985 var p = getCenterOffset(getLabelBox(ele));
30986
30987 if (ele.isNode()) {
30988 switch (ele.pstyle('text-halign').value) {
30989 case 'left':
30990 p.x = -bb.w;
30991 break;
30992
30993 case 'right':
30994 p.x = 0;
30995 break;
30996 }
30997
30998 switch (ele.pstyle('text-valign').value) {
30999 case 'top':
31000 p.y = -bb.h;
31001 break;
31002
31003 case 'bottom':
31004 p.y = 0;
31005 break;
31006 }
31007 }
31008
31009 return p;
31010 };
31011
31012 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31013 getKey: getStyleKey,
31014 doesEleInvalidateKey: backgroundTimestampHasChanged,
31015 drawElement: drawElement,
31016 getBoundingBox: getElementBox,
31017 getRotationPoint: getElementRotationPoint,
31018 getRotationOffset: getElementRotationOffset,
31019 allowEdgeTxrCaching: false,
31020 allowParentTxrCaching: false
31021 });
31022 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31023 getKey: getLabelKey,
31024 drawElement: drawLabel,
31025 getBoundingBox: getLabelBox,
31026 getRotationPoint: getLabelRotationPoint,
31027 getRotationOffset: getLabelRotationOffset,
31028 isVisible: isLabelVisibleAtScale
31029 });
31030 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31031 getKey: getSourceLabelKey,
31032 drawElement: drawSourceLabel,
31033 getBoundingBox: getSourceLabelBox,
31034 getRotationPoint: getSourceLabelRotationPoint,
31035 getRotationOffset: getSourceLabelRotationOffset,
31036 isVisible: isLabelVisibleAtScale
31037 });
31038 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31039 getKey: getTargetLabelKey,
31040 drawElement: drawTargetLabel,
31041 getBoundingBox: getTargetLabelBox,
31042 getRotationPoint: getTargetLabelRotationPoint,
31043 getRotationOffset: getTargetLabelRotationOffset,
31044 isVisible: isLabelVisibleAtScale
31045 });
31046 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31047 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31048 // each cache should check for sub-key diff to see that the update affects that cache particularly
31049 eleTxrCache.invalidateElements(eles);
31050 lblTxrCache.invalidateElements(eles);
31051 slbTxrCache.invalidateElements(eles);
31052 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31053
31054 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31055
31056 for (var _i = 0; _i < eles.length; _i++) {
31057 var _p = eles[_i]._private;
31058 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31059 }
31060 });
31061
31062 var refineInLayers = function refineInLayers(reqs) {
31063 for (var i = 0; i < reqs.length; i++) {
31064 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31065 }
31066 };
31067
31068 eleTxrCache.onDequeue(refineInLayers);
31069 lblTxrCache.onDequeue(refineInLayers);
31070 slbTxrCache.onDequeue(refineInLayers);
31071 tlbTxrCache.onDequeue(refineInLayers);
31072}
31073
31074CRp$a.redrawHint = function (group, bool) {
31075 var r = this;
31076
31077 switch (group) {
31078 case 'eles':
31079 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31080 break;
31081
31082 case 'drag':
31083 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31084 break;
31085
31086 case 'select':
31087 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31088 break;
31089 }
31090}; // whether to use Path2D caching for drawing
31091
31092
31093var pathsImpld = typeof Path2D !== 'undefined';
31094
31095CRp$a.path2dEnabled = function (on) {
31096 if (on === undefined) {
31097 return this.pathsEnabled;
31098 }
31099
31100 this.pathsEnabled = on ? true : false;
31101};
31102
31103CRp$a.usePaths = function () {
31104 return pathsImpld && this.pathsEnabled;
31105};
31106
31107CRp$a.setImgSmoothing = function (context, bool) {
31108 if (context.imageSmoothingEnabled != null) {
31109 context.imageSmoothingEnabled = bool;
31110 } else {
31111 context.webkitImageSmoothingEnabled = bool;
31112 context.mozImageSmoothingEnabled = bool;
31113 context.msImageSmoothingEnabled = bool;
31114 }
31115};
31116
31117CRp$a.getImgSmoothing = function (context) {
31118 if (context.imageSmoothingEnabled != null) {
31119 return context.imageSmoothingEnabled;
31120 } else {
31121 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31122 }
31123};
31124
31125CRp$a.makeOffscreenCanvas = function (width, height) {
31126 var canvas;
31127
31128 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
31129 canvas = new OffscreenCanvas(width, height);
31130 } else {
31131 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31132
31133 canvas.width = width;
31134 canvas.height = height;
31135 }
31136
31137 return canvas;
31138};
31139
31140[CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
31141 extend(CRp$a, props);
31142});
31143
31144var renderer = [{
31145 name: 'null',
31146 impl: NullRenderer
31147}, {
31148 name: 'base',
31149 impl: BR
31150}, {
31151 name: 'canvas',
31152 impl: CR
31153}];
31154
31155var incExts = [{
31156 type: 'layout',
31157 extensions: layout
31158}, {
31159 type: 'renderer',
31160 extensions: renderer
31161}];
31162
31163var extensions = {}; // registered modules for extensions, indexed by name
31164
31165var modules = {};
31166
31167function setExtension(type, name, registrant) {
31168 var ext = registrant;
31169
31170 var overrideErr = function overrideErr(field) {
31171 error('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31172 };
31173
31174 if (type === 'core') {
31175 if (Core.prototype[name]) {
31176 return overrideErr(name);
31177 } else {
31178 Core.prototype[name] = registrant;
31179 }
31180 } else if (type === 'collection') {
31181 if (Collection.prototype[name]) {
31182 return overrideErr(name);
31183 } else {
31184 Collection.prototype[name] = registrant;
31185 }
31186 } else if (type === 'layout') {
31187 // fill in missing layout functions in the prototype
31188 var Layout = function Layout(options) {
31189 this.options = options;
31190 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31191
31192 if (!plainObject(this._private)) {
31193 this._private = {};
31194 }
31195
31196 this._private.cy = options.cy;
31197 this._private.listeners = [];
31198 this.createEmitter();
31199 };
31200
31201 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31202 var optLayoutFns = [];
31203
31204 for (var i = 0; i < optLayoutFns.length; i++) {
31205 var fnName = optLayoutFns[i];
31206
31207 layoutProto[fnName] = layoutProto[fnName] || function () {
31208 return this;
31209 };
31210 } // either .start() or .run() is defined, so autogen the other
31211
31212
31213 if (layoutProto.start && !layoutProto.run) {
31214 layoutProto.run = function () {
31215 this.start();
31216 return this;
31217 };
31218 } else if (!layoutProto.start && layoutProto.run) {
31219 layoutProto.start = function () {
31220 this.run();
31221 return this;
31222 };
31223 }
31224
31225 var regStop = registrant.prototype.stop;
31226
31227 layoutProto.stop = function () {
31228 var opts = this.options;
31229
31230 if (opts && opts.animate) {
31231 var anis = this.animations;
31232
31233 if (anis) {
31234 for (var _i = 0; _i < anis.length; _i++) {
31235 anis[_i].stop();
31236 }
31237 }
31238 }
31239
31240 if (regStop) {
31241 regStop.call(this);
31242 } else {
31243 this.emit('layoutstop');
31244 }
31245
31246 return this;
31247 };
31248
31249 if (!layoutProto.destroy) {
31250 layoutProto.destroy = function () {
31251 return this;
31252 };
31253 }
31254
31255 layoutProto.cy = function () {
31256 return this._private.cy;
31257 };
31258
31259 var getCy = function getCy(layout) {
31260 return layout._private.cy;
31261 };
31262
31263 var emitterOpts = {
31264 addEventFields: function addEventFields(layout, evt) {
31265 evt.layout = layout;
31266 evt.cy = getCy(layout);
31267 evt.target = layout;
31268 },
31269 bubble: function bubble() {
31270 return true;
31271 },
31272 parent: function parent(layout) {
31273 return getCy(layout);
31274 }
31275 };
31276 extend(layoutProto, {
31277 createEmitter: function createEmitter() {
31278 this._private.emitter = new Emitter(emitterOpts, this);
31279 return this;
31280 },
31281 emitter: function emitter() {
31282 return this._private.emitter;
31283 },
31284 on: function on(evt, cb) {
31285 this.emitter().on(evt, cb);
31286 return this;
31287 },
31288 one: function one(evt, cb) {
31289 this.emitter().one(evt, cb);
31290 return this;
31291 },
31292 once: function once(evt, cb) {
31293 this.emitter().one(evt, cb);
31294 return this;
31295 },
31296 removeListener: function removeListener(evt, cb) {
31297 this.emitter().removeListener(evt, cb);
31298 return this;
31299 },
31300 removeAllListeners: function removeAllListeners() {
31301 this.emitter().removeAllListeners();
31302 return this;
31303 },
31304 emit: function emit(evt, params) {
31305 this.emitter().emit(evt, params);
31306 return this;
31307 }
31308 });
31309 define$3.eventAliasesOn(layoutProto);
31310 ext = Layout; // replace with our wrapped layout
31311 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31312 // user registered renderers inherit from base
31313 var BaseRenderer = getExtension('renderer', 'base');
31314 var bProto = BaseRenderer.prototype;
31315 var RegistrantRenderer = registrant;
31316 var rProto = registrant.prototype;
31317
31318 var Renderer = function Renderer() {
31319 BaseRenderer.apply(this, arguments);
31320 RegistrantRenderer.apply(this, arguments);
31321 };
31322
31323 var proto = Renderer.prototype;
31324
31325 for (var pName in bProto) {
31326 var pVal = bProto[pName];
31327 var existsInR = rProto[pName] != null;
31328
31329 if (existsInR) {
31330 return overrideErr(pName);
31331 }
31332
31333 proto[pName] = pVal; // take impl from base
31334 }
31335
31336 for (var _pName in rProto) {
31337 proto[_pName] = rProto[_pName]; // take impl from registrant
31338 }
31339
31340 bProto.clientFunctions.forEach(function (name) {
31341 proto[name] = proto[name] || function () {
31342 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31343 };
31344 });
31345 ext = Renderer;
31346 }
31347
31348 return setMap({
31349 map: extensions,
31350 keys: [type, name],
31351 value: ext
31352 });
31353}
31354
31355function getExtension(type, name) {
31356 return getMap({
31357 map: extensions,
31358 keys: [type, name]
31359 });
31360}
31361
31362function setModule(type, name, moduleType, moduleName, registrant) {
31363 return setMap({
31364 map: modules,
31365 keys: [type, name, moduleType, moduleName],
31366 value: registrant
31367 });
31368}
31369
31370function getModule(type, name, moduleType, moduleName) {
31371 return getMap({
31372 map: modules,
31373 keys: [type, name, moduleType, moduleName]
31374 });
31375}
31376
31377var extension = function extension() {
31378 // e.g. extension('renderer', 'svg')
31379 if (arguments.length === 2) {
31380 return getExtension.apply(null, arguments);
31381 } // e.g. extension('renderer', 'svg', { ... })
31382 else if (arguments.length === 3) {
31383 return setExtension.apply(null, arguments);
31384 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31385 else if (arguments.length === 4) {
31386 return getModule.apply(null, arguments);
31387 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31388 else if (arguments.length === 5) {
31389 return setModule.apply(null, arguments);
31390 } else {
31391 error('Invalid extension access syntax');
31392 }
31393}; // allows a core instance to access extensions internally
31394
31395
31396Core.prototype.extension = extension; // included extensions
31397
31398incExts.forEach(function (group) {
31399 group.extensions.forEach(function (ext) {
31400 setExtension(group.type, ext.name, ext.impl);
31401 });
31402});
31403
31404// (useful for init)
31405
31406var Stylesheet = function Stylesheet() {
31407 if (!(this instanceof Stylesheet)) {
31408 return new Stylesheet();
31409 }
31410
31411 this.length = 0;
31412};
31413
31414var sheetfn = Stylesheet.prototype;
31415
31416sheetfn.instanceString = function () {
31417 return 'stylesheet';
31418}; // just store the selector to be parsed later
31419
31420
31421sheetfn.selector = function (selector) {
31422 var i = this.length++;
31423 this[i] = {
31424 selector: selector,
31425 properties: []
31426 };
31427 return this; // chaining
31428}; // just store the property to be parsed later
31429
31430
31431sheetfn.css = function (name, value) {
31432 var i = this.length - 1;
31433
31434 if (string(name)) {
31435 this[i].properties.push({
31436 name: name,
31437 value: value
31438 });
31439 } else if (plainObject(name)) {
31440 var map = name;
31441 var propNames = Object.keys(map);
31442
31443 for (var j = 0; j < propNames.length; j++) {
31444 var key = propNames[j];
31445 var mapVal = map[key];
31446
31447 if (mapVal == null) {
31448 continue;
31449 }
31450
31451 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31452
31453 if (prop == null) {
31454 continue;
31455 }
31456
31457 var _name = prop.name;
31458 var _value = mapVal;
31459 this[i].properties.push({
31460 name: _name,
31461 value: _value
31462 });
31463 }
31464 }
31465
31466 return this; // chaining
31467};
31468
31469sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31470
31471sheetfn.generateStyle = function (cy) {
31472 var style = new Style(cy);
31473 return this.appendToStyle(style);
31474}; // append a dummy stylesheet object on a real style object
31475
31476
31477sheetfn.appendToStyle = function (style) {
31478 for (var i = 0; i < this.length; i++) {
31479 var context = this[i];
31480 var selector = context.selector;
31481 var props = context.properties;
31482 style.selector(selector); // apply selector
31483
31484 for (var j = 0; j < props.length; j++) {
31485 var prop = props[j];
31486 style.css(prop.name, prop.value); // apply property
31487 }
31488 }
31489
31490 return style;
31491};
31492
31493var version = "3.14.1";
31494
31495var cytoscape = function cytoscape(options) {
31496 // if no options specified, use default
31497 if (options === undefined) {
31498 options = {};
31499 } // create instance
31500
31501
31502 if (plainObject(options)) {
31503 return new Core(options);
31504 } // allow for registration of extensions
31505 else if (string(options)) {
31506 return extension.apply(extension, arguments);
31507 }
31508}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31509
31510
31511cytoscape.use = function (ext) {
31512 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31513
31514 args.unshift(cytoscape); // cytoscape is first arg to ext
31515
31516 ext.apply(null, args);
31517 return this;
31518};
31519
31520cytoscape.warnings = function (bool) {
31521 return warnings(bool);
31522}; // replaced by build system
31523
31524
31525cytoscape.version = version; // expose public apis (mostly for extensions)
31526
31527cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31528
31529module.exports = cytoscape;