UNPKG

863 kBJavaScriptView Raw
1/**
2 * Copyright (c) 2016-2021, The Cytoscape Consortium.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the “Software”), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8 * of the Software, and to permit persons to whom the Software is furnished to do
9 * so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20 * SOFTWARE.
21 */
22
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 !elementOrCollection(obj) && (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_HASH_SEED = 9261;
711var K = 65599; // 37 also works pretty well
712
713var DEFAULT_HASH_SEED_ALT = 5381;
714var hashIterableInts = function hashIterableInts(iterator) {
715 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
716 // sdbm/string-hash
717 var hash = seed;
718 var entry;
719
720 for (;;) {
721 entry = iterator.next();
722
723 if (entry.done) {
724 break;
725 }
726
727 hash = hash * K + entry.value | 0;
728 }
729
730 return hash;
731};
732var hashInt = function hashInt(num) {
733 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED;
734 // sdbm/string-hash
735 return seed * K + num | 0;
736};
737var hashIntAlt = function hashIntAlt(num) {
738 var seed = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : DEFAULT_HASH_SEED_ALT;
739 // djb2/string-hash
740 return (seed << 5) + seed + num | 0;
741};
742var combineHashes = function combineHashes(hash1, hash2) {
743 return hash1 * 0x200000 + hash2;
744};
745var combineHashesArray = function combineHashesArray(hashes) {
746 return hashes[0] * 0x200000 + hashes[1];
747};
748var hashArrays = function hashArrays(hashes1, hashes2) {
749 return [hashInt(hashes1[0], hashes2[0]), hashIntAlt(hashes1[1], hashes2[1])];
750};
751var hashIntsArray = function hashIntsArray(ints, seed) {
752 var entry = {
753 value: 0,
754 done: false
755 };
756 var i = 0;
757 var length = ints.length;
758 var iterator = {
759 next: function next() {
760 if (i < length) {
761 entry.value = ints[i++];
762 } else {
763 entry.done = true;
764 }
765
766 return entry;
767 }
768 };
769 return hashIterableInts(iterator, seed);
770};
771var hashString = function hashString(str, seed) {
772 var entry = {
773 value: 0,
774 done: false
775 };
776 var i = 0;
777 var length = str.length;
778 var iterator = {
779 next: function next() {
780 if (i < length) {
781 entry.value = str.charCodeAt(i++);
782 } else {
783 entry.done = true;
784 }
785
786 return entry;
787 }
788 };
789 return hashIterableInts(iterator, seed);
790};
791var hashStrings = function hashStrings() {
792 return hashStringsArray(arguments);
793};
794var hashStringsArray = function hashStringsArray(strs) {
795 var hash;
796
797 for (var i = 0; i < strs.length; i++) {
798 var str = strs[i];
799
800 if (i === 0) {
801 hash = hashString(str);
802 } else {
803 hash = hashString(str, hash);
804 }
805 }
806
807 return hash;
808};
809
810/*global console */
811var warningsEnabled = true;
812var warnSupported = console.warn != null; // eslint-disable-line no-console
813
814var traceSupported = console.trace != null; // eslint-disable-line no-console
815
816var MAX_INT = Number.MAX_SAFE_INTEGER || 9007199254740991;
817var trueify = function trueify() {
818 return true;
819};
820var falsify = function falsify() {
821 return false;
822};
823var zeroify = function zeroify() {
824 return 0;
825};
826var noop = function noop() {};
827var error = function error(msg) {
828 throw new Error(msg);
829};
830var warnings = function warnings(enabled) {
831 if (enabled !== undefined) {
832 warningsEnabled = !!enabled;
833 } else {
834 return warningsEnabled;
835 }
836};
837var warn = function warn(msg) {
838 /* eslint-disable no-console */
839 if (!warnings()) {
840 return;
841 }
842
843 if (warnSupported) {
844 console.warn(msg);
845 } else {
846 console.log(msg);
847
848 if (traceSupported) {
849 console.trace();
850 }
851 }
852};
853/* eslint-enable */
854
855var clone = function clone(obj) {
856 return extend({}, obj);
857}; // gets a shallow copy of the argument
858
859var copy = function copy(obj) {
860 if (obj == null) {
861 return obj;
862 }
863
864 if (array(obj)) {
865 return obj.slice();
866 } else if (plainObject(obj)) {
867 return clone(obj);
868 } else {
869 return obj;
870 }
871};
872var copyArray = function copyArray(arr) {
873 return arr.slice();
874};
875var uuid = function uuid(a, b
876/* placeholders */
877) {
878 for ( // loop :)
879 b = a = ''; // b - result , a - numeric letiable
880 a++ < 36; //
881 b += a * 51 & 52 // if "a" is not 9 or 14 or 19 or 24
882 ? // return a random number or 4
883 (a ^ 15 // if "a" is not 15
884 ? // genetate a random number from 0 to 15
885 8 ^ Math.random() * (a ^ 20 ? 16 : 4) // unless "a" is 20, in which case a random number from 8 to 11
886 : 4 // otherwise 4
887 ).toString(16) : '-' // in other cases (if "a" is 9,14,19,24) insert "-"
888 ) {
889 }
890
891 return b;
892};
893var _staticEmptyObject = {};
894var staticEmptyObject = function staticEmptyObject() {
895 return _staticEmptyObject;
896};
897var defaults = function defaults(_defaults) {
898 var keys = Object.keys(_defaults);
899 return function (opts) {
900 var filledOpts = {};
901
902 for (var i = 0; i < keys.length; i++) {
903 var key = keys[i];
904 var optVal = opts == null ? undefined : opts[key];
905 filledOpts[key] = optVal === undefined ? _defaults[key] : optVal;
906 }
907
908 return filledOpts;
909 };
910};
911var removeFromArray = function removeFromArray(arr, ele, oneCopy) {
912 for (var i = arr.length - 1; i >= 0; i--) {
913 if (arr[i] === ele) {
914 arr.splice(i, 1);
915
916 if (oneCopy) {
917 break;
918 }
919 }
920 }
921};
922var clearArray = function clearArray(arr) {
923 arr.splice(0, arr.length);
924};
925var push = function push(arr, otherArr) {
926 for (var i = 0; i < otherArr.length; i++) {
927 var el = otherArr[i];
928 arr.push(el);
929 }
930};
931var getPrefixedProperty = function getPrefixedProperty(obj, propName, prefix) {
932 if (prefix) {
933 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
934 }
935
936 return obj[propName];
937};
938var setPrefixedProperty = function setPrefixedProperty(obj, propName, prefix, value) {
939 if (prefix) {
940 propName = prependCamel(prefix, propName); // e.g. (labelWidth, source) => sourceLabelWidth
941 }
942
943 obj[propName] = value;
944};
945
946/* global Map */
947var ObjectMap =
948/*#__PURE__*/
949function () {
950 function ObjectMap() {
951 _classCallCheck(this, ObjectMap);
952
953 this._obj = {};
954 }
955
956 _createClass(ObjectMap, [{
957 key: "set",
958 value: function set(key, val) {
959 this._obj[key] = val;
960 return this;
961 }
962 }, {
963 key: "delete",
964 value: function _delete(key) {
965 this._obj[key] = undefined;
966 return this;
967 }
968 }, {
969 key: "clear",
970 value: function clear() {
971 this._obj = {};
972 }
973 }, {
974 key: "has",
975 value: function has(key) {
976 return this._obj[key] !== undefined;
977 }
978 }, {
979 key: "get",
980 value: function get(key) {
981 return this._obj[key];
982 }
983 }]);
984
985 return ObjectMap;
986}();
987
988var Map$1 = typeof Map !== 'undefined' ? Map : ObjectMap;
989
990/* global Set */
991var undef = "undefined" ;
992
993var ObjectSet =
994/*#__PURE__*/
995function () {
996 function ObjectSet(arrayOrObjectSet) {
997 _classCallCheck(this, ObjectSet);
998
999 this._obj = Object.create(null);
1000 this.size = 0;
1001
1002 if (arrayOrObjectSet != null) {
1003 var arr;
1004
1005 if (arrayOrObjectSet.instanceString != null && arrayOrObjectSet.instanceString() === this.instanceString()) {
1006 arr = arrayOrObjectSet.toArray();
1007 } else {
1008 arr = arrayOrObjectSet;
1009 }
1010
1011 for (var i = 0; i < arr.length; i++) {
1012 this.add(arr[i]);
1013 }
1014 }
1015 }
1016
1017 _createClass(ObjectSet, [{
1018 key: "instanceString",
1019 value: function instanceString() {
1020 return 'set';
1021 }
1022 }, {
1023 key: "add",
1024 value: function add(val) {
1025 var o = this._obj;
1026
1027 if (o[val] !== 1) {
1028 o[val] = 1;
1029 this.size++;
1030 }
1031 }
1032 }, {
1033 key: "delete",
1034 value: function _delete(val) {
1035 var o = this._obj;
1036
1037 if (o[val] === 1) {
1038 o[val] = 0;
1039 this.size--;
1040 }
1041 }
1042 }, {
1043 key: "clear",
1044 value: function clear() {
1045 this._obj = Object.create(null);
1046 }
1047 }, {
1048 key: "has",
1049 value: function has(val) {
1050 return this._obj[val] === 1;
1051 }
1052 }, {
1053 key: "toArray",
1054 value: function toArray() {
1055 var _this = this;
1056
1057 return Object.keys(this._obj).filter(function (key) {
1058 return _this.has(key);
1059 });
1060 }
1061 }, {
1062 key: "forEach",
1063 value: function forEach(callback, thisArg) {
1064 return this.toArray().forEach(callback, thisArg);
1065 }
1066 }]);
1067
1068 return ObjectSet;
1069}();
1070
1071var Set$1 = (typeof Set === "undefined" ? "undefined" : _typeof(Set)) !== undef ? Set : ObjectSet;
1072
1073var Element = function Element(cy, params) {
1074 var restore = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
1075
1076 if (cy === undefined || params === undefined || !core(cy)) {
1077 error('An element must have a core reference and parameters set');
1078 return;
1079 }
1080
1081 var group = params.group; // try to automatically infer the group if unspecified
1082
1083 if (group == null) {
1084 if (params.data && params.data.source != null && params.data.target != null) {
1085 group = 'edges';
1086 } else {
1087 group = 'nodes';
1088 }
1089 } // validate group
1090
1091
1092 if (group !== 'nodes' && group !== 'edges') {
1093 error('An element must be of type `nodes` or `edges`; you specified `' + group + '`');
1094 return;
1095 } // make the element array-like, just like a collection
1096
1097
1098 this.length = 1;
1099 this[0] = this; // NOTE: when something is added here, add also to ele.json()
1100
1101 var _p = this._private = {
1102 cy: cy,
1103 single: true,
1104 // indicates this is an element
1105 data: params.data || {},
1106 // data object
1107 position: params.position || {
1108 x: 0,
1109 y: 0
1110 },
1111 // (x, y) position pair
1112 autoWidth: undefined,
1113 // width and height of nodes calculated by the renderer when set to special 'auto' value
1114 autoHeight: undefined,
1115 autoPadding: undefined,
1116 compoundBoundsClean: false,
1117 // whether the compound dimensions need to be recalculated the next time dimensions are read
1118 listeners: [],
1119 // array of bound listeners
1120 group: group,
1121 // string; 'nodes' or 'edges'
1122 style: {},
1123 // properties as set by the style
1124 rstyle: {},
1125 // properties for style sent from the renderer to the core
1126 styleCxts: [],
1127 // applied style contexts from the styler
1128 styleKeys: {},
1129 // per-group keys of style property values
1130 removed: true,
1131 // whether it's inside the vis; true if removed (set true here since we call restore)
1132 selected: params.selected ? true : false,
1133 // whether it's selected
1134 selectable: params.selectable === undefined ? true : params.selectable ? true : false,
1135 // whether it's selectable
1136 locked: params.locked ? true : false,
1137 // whether the element is locked (cannot be moved)
1138 grabbed: false,
1139 // whether the element is grabbed by the mouse; renderer sets this privately
1140 grabbable: params.grabbable === undefined ? true : params.grabbable ? true : false,
1141 // whether the element can be grabbed
1142 pannable: params.pannable === undefined ? group === 'edges' ? true : false : params.pannable ? true : false,
1143 // whether the element has passthrough panning enabled
1144 active: false,
1145 // whether the element is active from user interaction
1146 classes: new Set$1(),
1147 // map ( className => true )
1148 animation: {
1149 // object for currently-running animations
1150 current: [],
1151 queue: []
1152 },
1153 rscratch: {},
1154 // object in which the renderer can store information
1155 scratch: params.scratch || {},
1156 // scratch objects
1157 edges: [],
1158 // array of connected edges
1159 children: [],
1160 // array of children
1161 parent: null,
1162 // parent ref
1163 traversalCache: {},
1164 // cache of output of traversal functions
1165 backgrounding: false,
1166 // whether background images are loading
1167 bbCache: null,
1168 // cache of the current bounding box
1169 bbCacheShift: {
1170 x: 0,
1171 y: 0
1172 },
1173 // shift applied to cached bb to be applied on next get
1174 bodyBounds: null,
1175 // bounds cache of element body, w/o overlay
1176 overlayBounds: null,
1177 // bounds cache of element body, including overlay
1178 labelBounds: {
1179 // bounds cache of labels
1180 all: null,
1181 source: null,
1182 target: null,
1183 main: null
1184 },
1185 arrowBounds: {
1186 // bounds cache of edge arrows
1187 source: null,
1188 target: null,
1189 'mid-source': null,
1190 'mid-target': null
1191 }
1192 };
1193
1194 if (_p.position.x == null) {
1195 _p.position.x = 0;
1196 }
1197
1198 if (_p.position.y == null) {
1199 _p.position.y = 0;
1200 } // renderedPosition overrides if specified
1201
1202
1203 if (params.renderedPosition) {
1204 var rpos = params.renderedPosition;
1205 var pan = cy.pan();
1206 var zoom = cy.zoom();
1207 _p.position = {
1208 x: (rpos.x - pan.x) / zoom,
1209 y: (rpos.y - pan.y) / zoom
1210 };
1211 }
1212
1213 var classes = [];
1214
1215 if (array(params.classes)) {
1216 classes = params.classes;
1217 } else if (string(params.classes)) {
1218 classes = params.classes.split(/\s+/);
1219 }
1220
1221 for (var i = 0, l = classes.length; i < l; i++) {
1222 var cls = classes[i];
1223
1224 if (!cls || cls === '') {
1225 continue;
1226 }
1227
1228 _p.classes.add(cls);
1229 }
1230
1231 this.createEmitter();
1232 var bypass = params.style || params.css;
1233
1234 if (bypass) {
1235 warn('Setting a `style` bypass at element creation should be done only when absolutely necessary. Try to use the stylesheet instead.');
1236 this.style(bypass);
1237 }
1238
1239 if (restore === undefined || restore) {
1240 this.restore();
1241 }
1242};
1243
1244var defineSearch = function defineSearch(params) {
1245 params = {
1246 bfs: params.bfs || !params.dfs,
1247 dfs: params.dfs || !params.bfs
1248 }; // from pseudocode on wikipedia
1249
1250 return function searchFn(roots, fn$1, directed) {
1251 var options;
1252
1253 if (plainObject(roots) && !elementOrCollection(roots)) {
1254 options = roots;
1255 roots = options.roots || options.root;
1256 fn$1 = options.visit;
1257 directed = options.directed;
1258 }
1259
1260 directed = arguments.length === 2 && !fn(fn$1) ? fn$1 : directed;
1261 fn$1 = fn(fn$1) ? fn$1 : function () {};
1262 var cy = this._private.cy;
1263 var v = roots = string(roots) ? this.filter(roots) : roots;
1264 var Q = [];
1265 var connectedNodes = [];
1266 var connectedBy = {};
1267 var id2depth = {};
1268 var V = {};
1269 var j = 0;
1270 var found;
1271
1272 var _this$byGroup = this.byGroup(),
1273 nodes = _this$byGroup.nodes,
1274 edges = _this$byGroup.edges; // enqueue v
1275
1276
1277 for (var i = 0; i < v.length; i++) {
1278 var vi = v[i];
1279 var viId = vi.id();
1280
1281 if (vi.isNode()) {
1282 Q.unshift(vi);
1283
1284 if (params.bfs) {
1285 V[viId] = true;
1286 connectedNodes.push(vi);
1287 }
1288
1289 id2depth[viId] = 0;
1290 }
1291 }
1292
1293 var _loop2 = function _loop2() {
1294 var v = params.bfs ? Q.shift() : Q.pop();
1295 var vId = v.id();
1296
1297 if (params.dfs) {
1298 if (V[vId]) {
1299 return "continue";
1300 }
1301
1302 V[vId] = true;
1303 connectedNodes.push(v);
1304 }
1305
1306 var depth = id2depth[vId];
1307 var prevEdge = connectedBy[vId];
1308 var src = prevEdge != null ? prevEdge.source() : null;
1309 var tgt = prevEdge != null ? prevEdge.target() : null;
1310 var prevNode = prevEdge == null ? undefined : v.same(src) ? tgt[0] : src[0];
1311 var ret = void 0;
1312 ret = fn$1(v, prevEdge, prevNode, j++, depth);
1313
1314 if (ret === true) {
1315 found = v;
1316 return "break";
1317 }
1318
1319 if (ret === false) {
1320 return "break";
1321 }
1322
1323 var vwEdges = v.connectedEdges().filter(function (e) {
1324 return (!directed || e.source().same(v)) && edges.has(e);
1325 });
1326
1327 for (var _i2 = 0; _i2 < vwEdges.length; _i2++) {
1328 var e = vwEdges[_i2];
1329 var w = e.connectedNodes().filter(function (n) {
1330 return !n.same(v) && nodes.has(n);
1331 });
1332 var wId = w.id();
1333
1334 if (w.length !== 0 && !V[wId]) {
1335 w = w[0];
1336 Q.push(w);
1337
1338 if (params.bfs) {
1339 V[wId] = true;
1340 connectedNodes.push(w);
1341 }
1342
1343 connectedBy[wId] = e;
1344 id2depth[wId] = id2depth[vId] + 1;
1345 }
1346 }
1347 };
1348
1349 _loop: while (Q.length !== 0) {
1350 var _ret = _loop2();
1351
1352 switch (_ret) {
1353 case "continue":
1354 continue;
1355
1356 case "break":
1357 break _loop;
1358 }
1359 }
1360
1361 var connectedEles = cy.collection();
1362
1363 for (var _i = 0; _i < connectedNodes.length; _i++) {
1364 var node = connectedNodes[_i];
1365 var edge = connectedBy[node.id()];
1366
1367 if (edge != null) {
1368 connectedEles.push(edge);
1369 }
1370
1371 connectedEles.push(node);
1372 }
1373
1374 return {
1375 path: cy.collection(connectedEles),
1376 found: cy.collection(found)
1377 };
1378 };
1379}; // search, spanning trees, etc
1380
1381
1382var elesfn = {
1383 breadthFirstSearch: defineSearch({
1384 bfs: true
1385 }),
1386 depthFirstSearch: defineSearch({
1387 dfs: true
1388 })
1389}; // nice, short mathemathical alias
1390
1391elesfn.bfs = elesfn.breadthFirstSearch;
1392elesfn.dfs = elesfn.depthFirstSearch;
1393
1394var dijkstraDefaults = defaults({
1395 root: null,
1396 weight: function weight(edge) {
1397 return 1;
1398 },
1399 directed: false
1400});
1401var elesfn$1 = {
1402 dijkstra: function dijkstra(options) {
1403 if (!plainObject(options)) {
1404 var args = arguments;
1405 options = {
1406 root: args[0],
1407 weight: args[1],
1408 directed: args[2]
1409 };
1410 }
1411
1412 var _dijkstraDefaults = dijkstraDefaults(options),
1413 root = _dijkstraDefaults.root,
1414 weight = _dijkstraDefaults.weight,
1415 directed = _dijkstraDefaults.directed;
1416
1417 var eles = this;
1418 var weightFn = weight;
1419 var source = string(root) ? this.filter(root)[0] : root[0];
1420 var dist = {};
1421 var prev = {};
1422 var knownDist = {};
1423
1424 var _this$byGroup = this.byGroup(),
1425 nodes = _this$byGroup.nodes,
1426 edges = _this$byGroup.edges;
1427
1428 edges.unmergeBy(function (ele) {
1429 return ele.isLoop();
1430 });
1431
1432 var getDist = function getDist(node) {
1433 return dist[node.id()];
1434 };
1435
1436 var setDist = function setDist(node, d) {
1437 dist[node.id()] = d;
1438 Q.updateItem(node);
1439 };
1440
1441 var Q = new Heap(function (a, b) {
1442 return getDist(a) - getDist(b);
1443 });
1444
1445 for (var i = 0; i < nodes.length; i++) {
1446 var node = nodes[i];
1447 dist[node.id()] = node.same(source) ? 0 : Infinity;
1448 Q.push(node);
1449 }
1450
1451 var distBetween = function distBetween(u, v) {
1452 var uvs = (directed ? u.edgesTo(v) : u.edgesWith(v)).intersect(edges);
1453 var smallestDistance = Infinity;
1454 var smallestEdge;
1455
1456 for (var _i = 0; _i < uvs.length; _i++) {
1457 var edge = uvs[_i];
1458
1459 var _weight = weightFn(edge);
1460
1461 if (_weight < smallestDistance || !smallestEdge) {
1462 smallestDistance = _weight;
1463 smallestEdge = edge;
1464 }
1465 }
1466
1467 return {
1468 edge: smallestEdge,
1469 dist: smallestDistance
1470 };
1471 };
1472
1473 while (Q.size() > 0) {
1474 var u = Q.pop();
1475 var smalletsDist = getDist(u);
1476 var uid = u.id();
1477 knownDist[uid] = smalletsDist;
1478
1479 if (smalletsDist === Infinity) {
1480 continue;
1481 }
1482
1483 var neighbors = u.neighborhood().intersect(nodes);
1484
1485 for (var _i2 = 0; _i2 < neighbors.length; _i2++) {
1486 var v = neighbors[_i2];
1487 var vid = v.id();
1488 var vDist = distBetween(u, v);
1489 var alt = smalletsDist + vDist.dist;
1490
1491 if (alt < getDist(v)) {
1492 setDist(v, alt);
1493 prev[vid] = {
1494 node: u,
1495 edge: vDist.edge
1496 };
1497 }
1498 } // for
1499
1500 } // while
1501
1502
1503 return {
1504 distanceTo: function distanceTo(node) {
1505 var target = string(node) ? nodes.filter(node)[0] : node[0];
1506 return knownDist[target.id()];
1507 },
1508 pathTo: function pathTo(node) {
1509 var target = string(node) ? nodes.filter(node)[0] : node[0];
1510 var S = [];
1511 var u = target;
1512 var uid = u.id();
1513
1514 if (target.length > 0) {
1515 S.unshift(target);
1516
1517 while (prev[uid]) {
1518 var p = prev[uid];
1519 S.unshift(p.edge);
1520 S.unshift(p.node);
1521 u = p.node;
1522 uid = u.id();
1523 }
1524 }
1525
1526 return eles.spawn(S);
1527 }
1528 };
1529 }
1530};
1531
1532var elesfn$2 = {
1533 // kruskal's algorithm (finds min spanning tree, assuming undirected graph)
1534 // implemented from pseudocode from wikipedia
1535 kruskal: function kruskal(weightFn) {
1536 weightFn = weightFn || function (edge) {
1537 return 1;
1538 };
1539
1540 var _this$byGroup = this.byGroup(),
1541 nodes = _this$byGroup.nodes,
1542 edges = _this$byGroup.edges;
1543
1544 var numNodes = nodes.length;
1545 var forest = new Array(numNodes);
1546 var A = nodes; // assumes byGroup() creates new collections that can be safely mutated
1547
1548 var findSetIndex = function findSetIndex(ele) {
1549 for (var i = 0; i < forest.length; i++) {
1550 var eles = forest[i];
1551
1552 if (eles.has(ele)) {
1553 return i;
1554 }
1555 }
1556 }; // start with one forest per node
1557
1558
1559 for (var i = 0; i < numNodes; i++) {
1560 forest[i] = this.spawn(nodes[i]);
1561 }
1562
1563 var S = edges.sort(function (a, b) {
1564 return weightFn(a) - weightFn(b);
1565 });
1566
1567 for (var _i = 0; _i < S.length; _i++) {
1568 var edge = S[_i];
1569 var u = edge.source()[0];
1570 var v = edge.target()[0];
1571 var setUIndex = findSetIndex(u);
1572 var setVIndex = findSetIndex(v);
1573 var setU = forest[setUIndex];
1574 var setV = forest[setVIndex];
1575
1576 if (setUIndex !== setVIndex) {
1577 A.merge(edge); // combine forests for u and v
1578
1579 setU.merge(setV);
1580 forest.splice(setVIndex, 1);
1581 }
1582 }
1583
1584 return A;
1585 }
1586};
1587
1588var aStarDefaults = defaults({
1589 root: null,
1590 goal: null,
1591 weight: function weight(edge) {
1592 return 1;
1593 },
1594 heuristic: function heuristic(edge) {
1595 return 0;
1596 },
1597 directed: false
1598});
1599var elesfn$3 = {
1600 // Implemented from pseudocode from wikipedia
1601 aStar: function aStar(options) {
1602 var cy = this.cy();
1603
1604 var _aStarDefaults = aStarDefaults(options),
1605 root = _aStarDefaults.root,
1606 goal = _aStarDefaults.goal,
1607 heuristic = _aStarDefaults.heuristic,
1608 directed = _aStarDefaults.directed,
1609 weight = _aStarDefaults.weight;
1610
1611 root = cy.collection(root)[0];
1612 goal = cy.collection(goal)[0];
1613 var sid = root.id();
1614 var tid = goal.id();
1615 var gScore = {};
1616 var fScore = {};
1617 var closedSetIds = {};
1618 var openSet = new Heap(function (a, b) {
1619 return fScore[a.id()] - fScore[b.id()];
1620 });
1621 var openSetIds = new Set$1();
1622 var cameFrom = {};
1623 var cameFromEdge = {};
1624
1625 var addToOpenSet = function addToOpenSet(ele, id) {
1626 openSet.push(ele);
1627 openSetIds.add(id);
1628 };
1629
1630 var cMin, cMinId;
1631
1632 var popFromOpenSet = function popFromOpenSet() {
1633 cMin = openSet.pop();
1634 cMinId = cMin.id();
1635 openSetIds["delete"](cMinId);
1636 };
1637
1638 var isInOpenSet = function isInOpenSet(id) {
1639 return openSetIds.has(id);
1640 };
1641
1642 addToOpenSet(root, sid);
1643 gScore[sid] = 0;
1644 fScore[sid] = heuristic(root); // Counter
1645
1646 var steps = 0; // Main loop
1647
1648 while (openSet.size() > 0) {
1649 popFromOpenSet();
1650 steps++; // If we've found our goal, then we are done
1651
1652 if (cMinId === tid) {
1653 var path = [];
1654 var pathNode = goal;
1655 var pathNodeId = tid;
1656 var pathEdge = cameFromEdge[pathNodeId];
1657
1658 for (;;) {
1659 path.unshift(pathNode);
1660
1661 if (pathEdge != null) {
1662 path.unshift(pathEdge);
1663 }
1664
1665 pathNode = cameFrom[pathNodeId];
1666
1667 if (pathNode == null) {
1668 break;
1669 }
1670
1671 pathNodeId = pathNode.id();
1672 pathEdge = cameFromEdge[pathNodeId];
1673 }
1674
1675 return {
1676 found: true,
1677 distance: gScore[cMinId],
1678 path: this.spawn(path),
1679 steps: steps
1680 };
1681 } // Add cMin to processed nodes
1682
1683
1684 closedSetIds[cMinId] = true; // Update scores for neighbors of cMin
1685 // Take into account if graph is directed or not
1686
1687 var vwEdges = cMin._private.edges;
1688
1689 for (var i = 0; i < vwEdges.length; i++) {
1690 var e = vwEdges[i]; // edge must be in set of calling eles
1691
1692 if (!this.hasElementWithId(e.id())) {
1693 continue;
1694 } // cMin must be the source of edge if directed
1695
1696
1697 if (directed && e.data('source') !== cMinId) {
1698 continue;
1699 }
1700
1701 var wSrc = e.source();
1702 var wTgt = e.target();
1703 var w = wSrc.id() !== cMinId ? wSrc : wTgt;
1704 var wid = w.id(); // node must be in set of calling eles
1705
1706 if (!this.hasElementWithId(wid)) {
1707 continue;
1708 } // if node is in closedSet, ignore it
1709
1710
1711 if (closedSetIds[wid]) {
1712 continue;
1713 } // New tentative score for node w
1714
1715
1716 var tempScore = gScore[cMinId] + weight(e); // Update gScore for node w if:
1717 // w not present in openSet
1718 // OR
1719 // tentative gScore is less than previous value
1720 // w not in openSet
1721
1722 if (!isInOpenSet(wid)) {
1723 gScore[wid] = tempScore;
1724 fScore[wid] = tempScore + heuristic(w);
1725 addToOpenSet(w, wid);
1726 cameFrom[wid] = cMin;
1727 cameFromEdge[wid] = e;
1728 continue;
1729 } // w already in openSet, but with greater gScore
1730
1731
1732 if (tempScore < gScore[wid]) {
1733 gScore[wid] = tempScore;
1734 fScore[wid] = tempScore + heuristic(w);
1735 cameFrom[wid] = cMin;
1736 cameFromEdge[wid] = e;
1737 }
1738 } // End of neighbors update
1739
1740 } // End of main loop
1741 // If we've reached here, then we've not reached our goal
1742
1743
1744 return {
1745 found: false,
1746 distance: undefined,
1747 path: undefined,
1748 steps: steps
1749 };
1750 }
1751}; // elesfn
1752
1753var floydWarshallDefaults = defaults({
1754 weight: function weight(edge) {
1755 return 1;
1756 },
1757 directed: false
1758});
1759var elesfn$4 = {
1760 // Implemented from pseudocode from wikipedia
1761 floydWarshall: function floydWarshall(options) {
1762 var cy = this.cy();
1763
1764 var _floydWarshallDefault = floydWarshallDefaults(options),
1765 weight = _floydWarshallDefault.weight,
1766 directed = _floydWarshallDefault.directed;
1767
1768 var weightFn = weight;
1769
1770 var _this$byGroup = this.byGroup(),
1771 nodes = _this$byGroup.nodes,
1772 edges = _this$byGroup.edges;
1773
1774 var N = nodes.length;
1775 var Nsq = N * N;
1776
1777 var indexOf = function indexOf(node) {
1778 return nodes.indexOf(node);
1779 };
1780
1781 var atIndex = function atIndex(i) {
1782 return nodes[i];
1783 }; // Initialize distance matrix
1784
1785
1786 var dist = new Array(Nsq);
1787
1788 for (var n = 0; n < Nsq; n++) {
1789 var j = n % N;
1790 var i = (n - j) / N;
1791
1792 if (i === j) {
1793 dist[n] = 0;
1794 } else {
1795 dist[n] = Infinity;
1796 }
1797 } // Initialize matrix used for path reconstruction
1798 // Initialize distance matrix
1799
1800
1801 var next = new Array(Nsq);
1802 var edgeNext = new Array(Nsq); // Process edges
1803
1804 for (var _i = 0; _i < edges.length; _i++) {
1805 var edge = edges[_i];
1806 var src = edge.source()[0];
1807 var tgt = edge.target()[0];
1808
1809 if (src === tgt) {
1810 continue;
1811 } // exclude loops
1812
1813
1814 var s = indexOf(src);
1815 var t = indexOf(tgt);
1816 var st = s * N + t; // source to target index
1817
1818 var _weight = weightFn(edge); // Check if already process another edge between same 2 nodes
1819
1820
1821 if (dist[st] > _weight) {
1822 dist[st] = _weight;
1823 next[st] = t;
1824 edgeNext[st] = edge;
1825 } // If undirected graph, process 'reversed' edge
1826
1827
1828 if (!directed) {
1829 var ts = t * N + s; // target to source index
1830
1831 if (!directed && dist[ts] > _weight) {
1832 dist[ts] = _weight;
1833 next[ts] = s;
1834 edgeNext[ts] = edge;
1835 }
1836 }
1837 } // Main loop
1838
1839
1840 for (var k = 0; k < N; k++) {
1841 for (var _i2 = 0; _i2 < N; _i2++) {
1842 var ik = _i2 * N + k;
1843
1844 for (var _j = 0; _j < N; _j++) {
1845 var ij = _i2 * N + _j;
1846 var kj = k * N + _j;
1847
1848 if (dist[ik] + dist[kj] < dist[ij]) {
1849 dist[ij] = dist[ik] + dist[kj];
1850 next[ij] = next[ik];
1851 }
1852 }
1853 }
1854 }
1855
1856 var getArgEle = function getArgEle(ele) {
1857 return (string(ele) ? cy.filter(ele) : ele)[0];
1858 };
1859
1860 var indexOfArgEle = function indexOfArgEle(ele) {
1861 return indexOf(getArgEle(ele));
1862 };
1863
1864 var res = {
1865 distance: function distance(from, to) {
1866 var i = indexOfArgEle(from);
1867 var j = indexOfArgEle(to);
1868 return dist[i * N + j];
1869 },
1870 path: function path(from, to) {
1871 var i = indexOfArgEle(from);
1872 var j = indexOfArgEle(to);
1873 var fromNode = atIndex(i);
1874
1875 if (i === j) {
1876 return fromNode.collection();
1877 }
1878
1879 if (next[i * N + j] == null) {
1880 return cy.collection();
1881 }
1882
1883 var path = cy.collection();
1884 var prev = i;
1885 var edge;
1886 path.merge(fromNode);
1887
1888 while (i !== j) {
1889 prev = i;
1890 i = next[i * N + j];
1891 edge = edgeNext[prev * N + i];
1892 path.merge(edge);
1893 path.merge(atIndex(i));
1894 }
1895
1896 return path;
1897 }
1898 };
1899 return res;
1900 } // floydWarshall
1901
1902}; // elesfn
1903
1904var bellmanFordDefaults = defaults({
1905 weight: function weight(edge) {
1906 return 1;
1907 },
1908 directed: false,
1909 root: null
1910});
1911var elesfn$5 = {
1912 // Implemented from pseudocode from wikipedia
1913 bellmanFord: function bellmanFord(options) {
1914 var _this = this;
1915
1916 var _bellmanFordDefaults = bellmanFordDefaults(options),
1917 weight = _bellmanFordDefaults.weight,
1918 directed = _bellmanFordDefaults.directed,
1919 root = _bellmanFordDefaults.root;
1920
1921 var weightFn = weight;
1922 var eles = this;
1923 var cy = this.cy();
1924
1925 var _this$byGroup = this.byGroup(),
1926 edges = _this$byGroup.edges,
1927 nodes = _this$byGroup.nodes;
1928
1929 var numNodes = nodes.length;
1930 var infoMap = new Map$1();
1931 var hasNegativeWeightCycle = false;
1932 var negativeWeightCycles = [];
1933 root = cy.collection(root)[0]; // in case selector passed
1934
1935 edges.unmergeBy(function (edge) {
1936 return edge.isLoop();
1937 });
1938 var numEdges = edges.length;
1939
1940 var getInfo = function getInfo(node) {
1941 var obj = infoMap.get(node.id());
1942
1943 if (!obj) {
1944 obj = {};
1945 infoMap.set(node.id(), obj);
1946 }
1947
1948 return obj;
1949 };
1950
1951 var getNodeFromTo = function getNodeFromTo(to) {
1952 return (string(to) ? cy.$(to) : to)[0];
1953 };
1954
1955 var distanceTo = function distanceTo(to) {
1956 return getInfo(getNodeFromTo(to)).dist;
1957 };
1958
1959 var pathTo = function pathTo(to) {
1960 var thisStart = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : root;
1961 var end = getNodeFromTo(to);
1962 var path = [];
1963 var node = end;
1964
1965 for (;;) {
1966 if (node == null) {
1967 return _this.spawn();
1968 }
1969
1970 var _getInfo = getInfo(node),
1971 edge = _getInfo.edge,
1972 pred = _getInfo.pred;
1973
1974 path.unshift(node[0]);
1975
1976 if (node.same(thisStart) && path.length > 0) {
1977 break;
1978 }
1979
1980 if (edge != null) {
1981 path.unshift(edge);
1982 }
1983
1984 node = pred;
1985 }
1986
1987 return eles.spawn(path);
1988 }; // Initializations { dist, pred, edge }
1989
1990
1991 for (var i = 0; i < numNodes; i++) {
1992 var node = nodes[i];
1993 var info = getInfo(node);
1994
1995 if (node.same(root)) {
1996 info.dist = 0;
1997 } else {
1998 info.dist = Infinity;
1999 }
2000
2001 info.pred = null;
2002 info.edge = null;
2003 } // Edges relaxation
2004
2005
2006 var replacedEdge = false;
2007
2008 var checkForEdgeReplacement = function checkForEdgeReplacement(node1, node2, edge, info1, info2, weight) {
2009 var dist = info1.dist + weight;
2010
2011 if (dist < info2.dist && !edge.same(info1.edge)) {
2012 info2.dist = dist;
2013 info2.pred = node1;
2014 info2.edge = edge;
2015 replacedEdge = true;
2016 }
2017 };
2018
2019 for (var _i = 1; _i < numNodes; _i++) {
2020 replacedEdge = false;
2021
2022 for (var e = 0; e < numEdges; e++) {
2023 var edge = edges[e];
2024 var src = edge.source();
2025 var tgt = edge.target();
2026
2027 var _weight = weightFn(edge);
2028
2029 var srcInfo = getInfo(src);
2030 var tgtInfo = getInfo(tgt);
2031 checkForEdgeReplacement(src, tgt, edge, srcInfo, tgtInfo, _weight); // If undirected graph, we need to take into account the 'reverse' edge
2032
2033 if (!directed) {
2034 checkForEdgeReplacement(tgt, src, edge, tgtInfo, srcInfo, _weight);
2035 }
2036 }
2037
2038 if (!replacedEdge) {
2039 break;
2040 }
2041 }
2042
2043 if (replacedEdge) {
2044 // Check for negative weight cycles
2045 for (var _e = 0; _e < numEdges; _e++) {
2046 var _edge = edges[_e];
2047
2048 var _src = _edge.source();
2049
2050 var _tgt = _edge.target();
2051
2052 var _weight2 = weightFn(_edge);
2053
2054 var srcDist = getInfo(_src).dist;
2055 var tgtDist = getInfo(_tgt).dist;
2056
2057 if (srcDist + _weight2 < tgtDist || !directed && tgtDist + _weight2 < srcDist) {
2058 warn('Graph contains a negative weight cycle for Bellman-Ford');
2059 hasNegativeWeightCycle = true;
2060 break;
2061 }
2062 }
2063 }
2064
2065 return {
2066 distanceTo: distanceTo,
2067 pathTo: pathTo,
2068 hasNegativeWeightCycle: hasNegativeWeightCycle,
2069 negativeWeightCycles: negativeWeightCycles
2070 };
2071 } // bellmanFord
2072
2073}; // elesfn
2074
2075var sqrt2 = Math.sqrt(2); // Function which colapses 2 (meta) nodes into one
2076// Updates the remaining edge lists
2077// Receives as a paramater the edge which causes the collapse
2078
2079var collapse = function collapse(edgeIndex, nodeMap, remainingEdges) {
2080 if (remainingEdges.length === 0) {
2081 error("Karger-Stein must be run on a connected (sub)graph");
2082 }
2083
2084 var edgeInfo = remainingEdges[edgeIndex];
2085 var sourceIn = edgeInfo[1];
2086 var targetIn = edgeInfo[2];
2087 var partition1 = nodeMap[sourceIn];
2088 var partition2 = nodeMap[targetIn];
2089 var newEdges = remainingEdges; // re-use array
2090 // Delete all edges between partition1 and partition2
2091
2092 for (var i = newEdges.length - 1; i >= 0; i--) {
2093 var edge = newEdges[i];
2094 var src = edge[1];
2095 var tgt = edge[2];
2096
2097 if (nodeMap[src] === partition1 && nodeMap[tgt] === partition2 || nodeMap[src] === partition2 && nodeMap[tgt] === partition1) {
2098 newEdges.splice(i, 1);
2099 }
2100 } // All edges pointing to partition2 should now point to partition1
2101
2102
2103 for (var _i = 0; _i < newEdges.length; _i++) {
2104 var _edge = newEdges[_i];
2105
2106 if (_edge[1] === partition2) {
2107 // Check source
2108 newEdges[_i] = _edge.slice(); // copy
2109
2110 newEdges[_i][1] = partition1;
2111 } else if (_edge[2] === partition2) {
2112 // Check target
2113 newEdges[_i] = _edge.slice(); // copy
2114
2115 newEdges[_i][2] = partition1;
2116 }
2117 } // Move all nodes from partition2 to partition1
2118
2119
2120 for (var _i2 = 0; _i2 < nodeMap.length; _i2++) {
2121 if (nodeMap[_i2] === partition2) {
2122 nodeMap[_i2] = partition1;
2123 }
2124 }
2125
2126 return newEdges;
2127}; // Contracts a graph until we reach a certain number of meta nodes
2128
2129
2130var contractUntil = function contractUntil(metaNodeMap, remainingEdges, size, sizeLimit) {
2131 while (size > sizeLimit) {
2132 // Choose an edge randomly
2133 var edgeIndex = Math.floor(Math.random() * remainingEdges.length); // Collapse graph based on edge
2134
2135 remainingEdges = collapse(edgeIndex, metaNodeMap, remainingEdges);
2136 size--;
2137 }
2138
2139 return remainingEdges;
2140};
2141
2142var elesfn$6 = {
2143 // Computes the minimum cut of an undirected graph
2144 // Returns the correct answer with high probability
2145 kargerStein: function kargerStein() {
2146 var _this = this;
2147
2148 var _this$byGroup = this.byGroup(),
2149 nodes = _this$byGroup.nodes,
2150 edges = _this$byGroup.edges;
2151
2152 edges.unmergeBy(function (edge) {
2153 return edge.isLoop();
2154 });
2155 var numNodes = nodes.length;
2156 var numEdges = edges.length;
2157 var numIter = Math.ceil(Math.pow(Math.log(numNodes) / Math.LN2, 2));
2158 var stopSize = Math.floor(numNodes / sqrt2);
2159
2160 if (numNodes < 2) {
2161 error('At least 2 nodes are required for Karger-Stein algorithm');
2162 return undefined;
2163 } // Now store edge destination as indexes
2164 // Format for each edge (edge index, source node index, target node index)
2165
2166
2167 var edgeIndexes = [];
2168
2169 for (var i = 0; i < numEdges; i++) {
2170 var e = edges[i];
2171 edgeIndexes.push([i, nodes.indexOf(e.source()), nodes.indexOf(e.target())]);
2172 } // We will store the best cut found here
2173
2174
2175 var minCutSize = Infinity;
2176 var minCutEdgeIndexes = [];
2177 var minCutNodeMap = new Array(numNodes); // Initial meta node partition
2178
2179 var metaNodeMap = new Array(numNodes);
2180 var metaNodeMap2 = new Array(numNodes);
2181
2182 var copyNodesMap = function copyNodesMap(from, to) {
2183 for (var _i3 = 0; _i3 < numNodes; _i3++) {
2184 to[_i3] = from[_i3];
2185 }
2186 }; // Main loop
2187
2188
2189 for (var iter = 0; iter <= numIter; iter++) {
2190 // Reset meta node partition
2191 for (var _i4 = 0; _i4 < numNodes; _i4++) {
2192 metaNodeMap[_i4] = _i4;
2193 } // Contract until stop point (stopSize nodes)
2194
2195
2196 var edgesState = contractUntil(metaNodeMap, edgeIndexes.slice(), numNodes, stopSize);
2197 var edgesState2 = edgesState.slice(); // copy
2198 // Create a copy of the colapsed nodes state
2199
2200 copyNodesMap(metaNodeMap, metaNodeMap2); // Run 2 iterations starting in the stop state
2201
2202 var res1 = contractUntil(metaNodeMap, edgesState, stopSize, 2);
2203 var res2 = contractUntil(metaNodeMap2, edgesState2, stopSize, 2); // Is any of the 2 results the best cut so far?
2204
2205 if (res1.length <= res2.length && res1.length < minCutSize) {
2206 minCutSize = res1.length;
2207 minCutEdgeIndexes = res1;
2208 copyNodesMap(metaNodeMap, minCutNodeMap);
2209 } else if (res2.length <= res1.length && res2.length < minCutSize) {
2210 minCutSize = res2.length;
2211 minCutEdgeIndexes = res2;
2212 copyNodesMap(metaNodeMap2, minCutNodeMap);
2213 }
2214 } // end of main loop
2215 // Construct result
2216
2217
2218 var cut = this.spawn(minCutEdgeIndexes.map(function (e) {
2219 return edges[e[0]];
2220 }));
2221 var partition1 = this.spawn();
2222 var partition2 = this.spawn(); // traverse metaNodeMap for best cut
2223
2224 var witnessNodePartition = minCutNodeMap[0];
2225
2226 for (var _i5 = 0; _i5 < minCutNodeMap.length; _i5++) {
2227 var partitionId = minCutNodeMap[_i5];
2228 var node = nodes[_i5];
2229
2230 if (partitionId === witnessNodePartition) {
2231 partition1.merge(node);
2232 } else {
2233 partition2.merge(node);
2234 }
2235 } // construct components corresponding to each disjoint subset of nodes
2236
2237
2238 var constructComponent = function constructComponent(subset) {
2239 var component = _this.spawn();
2240
2241 subset.forEach(function (node) {
2242 component.merge(node);
2243 node.connectedEdges().forEach(function (edge) {
2244 // ensure edge is within calling collection and edge is not in cut
2245 if (_this.contains(edge) && !cut.contains(edge)) {
2246 component.merge(edge);
2247 }
2248 });
2249 });
2250 return component;
2251 };
2252
2253 var components = [constructComponent(partition1), constructComponent(partition2)];
2254 var ret = {
2255 cut: cut,
2256 components: components,
2257 // n.b. partitions are included to be compatible with the old api spec
2258 // (could be removed in a future major version)
2259 partition1: partition1,
2260 partition2: partition2
2261 };
2262 return ret;
2263 }
2264}; // elesfn
2265
2266var copyPosition = function copyPosition(p) {
2267 return {
2268 x: p.x,
2269 y: p.y
2270 };
2271};
2272var modelToRenderedPosition = function modelToRenderedPosition(p, zoom, pan) {
2273 return {
2274 x: p.x * zoom + pan.x,
2275 y: p.y * zoom + pan.y
2276 };
2277};
2278var renderedToModelPosition = function renderedToModelPosition(p, zoom, pan) {
2279 return {
2280 x: (p.x - pan.x) / zoom,
2281 y: (p.y - pan.y) / zoom
2282 };
2283};
2284var array2point = function array2point(arr) {
2285 return {
2286 x: arr[0],
2287 y: arr[1]
2288 };
2289};
2290var min = function min(arr) {
2291 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2292 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2293 var min = Infinity;
2294
2295 for (var i = begin; i < end; i++) {
2296 var val = arr[i];
2297
2298 if (isFinite(val)) {
2299 min = Math.min(val, min);
2300 }
2301 }
2302
2303 return min;
2304};
2305var max = function max(arr) {
2306 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2307 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2308 var max = -Infinity;
2309
2310 for (var i = begin; i < end; i++) {
2311 var val = arr[i];
2312
2313 if (isFinite(val)) {
2314 max = Math.max(val, max);
2315 }
2316 }
2317
2318 return max;
2319};
2320var mean = function mean(arr) {
2321 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2322 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2323 var total = 0;
2324 var n = 0;
2325
2326 for (var i = begin; i < end; i++) {
2327 var val = arr[i];
2328
2329 if (isFinite(val)) {
2330 total += val;
2331 n++;
2332 }
2333 }
2334
2335 return total / n;
2336};
2337var median = function median(arr) {
2338 var begin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2339 var end = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : arr.length;
2340 var copy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
2341 var sort = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
2342 var includeHoles = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
2343
2344 if (copy) {
2345 arr = arr.slice(begin, end);
2346 } else {
2347 if (end < arr.length) {
2348 arr.splice(end, arr.length - end);
2349 }
2350
2351 if (begin > 0) {
2352 arr.splice(0, begin);
2353 }
2354 } // all non finite (e.g. Infinity, NaN) elements must be -Infinity so they go to the start
2355
2356
2357 var off = 0; // offset from non-finite values
2358
2359 for (var i = arr.length - 1; i >= 0; i--) {
2360 var v = arr[i];
2361
2362 if (includeHoles) {
2363 if (!isFinite(v)) {
2364 arr[i] = -Infinity;
2365 off++;
2366 }
2367 } else {
2368 // just remove it if we don't want to consider holes
2369 arr.splice(i, 1);
2370 }
2371 }
2372
2373 if (sort) {
2374 arr.sort(function (a, b) {
2375 return a - b;
2376 }); // requires copy = true if you don't want to change the orig
2377 }
2378
2379 var len = arr.length;
2380 var mid = Math.floor(len / 2);
2381
2382 if (len % 2 !== 0) {
2383 return arr[mid + 1 + off];
2384 } else {
2385 return (arr[mid - 1 + off] + arr[mid + off]) / 2;
2386 }
2387};
2388var deg2rad = function deg2rad(deg) {
2389 return Math.PI * deg / 180;
2390};
2391var getAngleFromDisp = function getAngleFromDisp(dispX, dispY) {
2392 return Math.atan2(dispY, dispX) - Math.PI / 2;
2393};
2394var log2 = Math.log2 || function (n) {
2395 return Math.log(n) / Math.log(2);
2396};
2397var signum = function signum(x) {
2398 if (x > 0) {
2399 return 1;
2400 } else if (x < 0) {
2401 return -1;
2402 } else {
2403 return 0;
2404 }
2405};
2406var dist = function dist(p1, p2) {
2407 return Math.sqrt(sqdist(p1, p2));
2408};
2409var sqdist = function sqdist(p1, p2) {
2410 var dx = p2.x - p1.x;
2411 var dy = p2.y - p1.y;
2412 return dx * dx + dy * dy;
2413};
2414var inPlaceSumNormalize = function inPlaceSumNormalize(v) {
2415 var length = v.length; // First, get sum of all elements
2416
2417 var total = 0;
2418
2419 for (var i = 0; i < length; i++) {
2420 total += v[i];
2421 } // Now, divide each by the sum of all elements
2422
2423
2424 for (var _i = 0; _i < length; _i++) {
2425 v[_i] = v[_i] / total;
2426 }
2427
2428 return v;
2429};
2430
2431var qbezierAt = function qbezierAt(p0, p1, p2, t) {
2432 return (1 - t) * (1 - t) * p0 + 2 * (1 - t) * t * p1 + t * t * p2;
2433};
2434var qbezierPtAt = function qbezierPtAt(p0, p1, p2, t) {
2435 return {
2436 x: qbezierAt(p0.x, p1.x, p2.x, t),
2437 y: qbezierAt(p0.y, p1.y, p2.y, t)
2438 };
2439};
2440var lineAt = function lineAt(p0, p1, t, d) {
2441 var vec = {
2442 x: p1.x - p0.x,
2443 y: p1.y - p0.y
2444 };
2445 var vecDist = dist(p0, p1);
2446 var normVec = {
2447 x: vec.x / vecDist,
2448 y: vec.y / vecDist
2449 };
2450 t = t == null ? 0 : t;
2451 d = d != null ? d : t * vecDist;
2452 return {
2453 x: p0.x + normVec.x * d,
2454 y: p0.y + normVec.y * d
2455 };
2456};
2457var bound = function bound(min, val, max) {
2458 return Math.max(min, Math.min(max, val));
2459}; // makes a full bb (x1, y1, x2, y2, w, h) from implicit params
2460
2461var makeBoundingBox = function makeBoundingBox(bb) {
2462 if (bb == null) {
2463 return {
2464 x1: Infinity,
2465 y1: Infinity,
2466 x2: -Infinity,
2467 y2: -Infinity,
2468 w: 0,
2469 h: 0
2470 };
2471 } else if (bb.x1 != null && bb.y1 != null) {
2472 if (bb.x2 != null && bb.y2 != null && bb.x2 >= bb.x1 && bb.y2 >= bb.y1) {
2473 return {
2474 x1: bb.x1,
2475 y1: bb.y1,
2476 x2: bb.x2,
2477 y2: bb.y2,
2478 w: bb.x2 - bb.x1,
2479 h: bb.y2 - bb.y1
2480 };
2481 } else if (bb.w != null && bb.h != null && bb.w >= 0 && bb.h >= 0) {
2482 return {
2483 x1: bb.x1,
2484 y1: bb.y1,
2485 x2: bb.x1 + bb.w,
2486 y2: bb.y1 + bb.h,
2487 w: bb.w,
2488 h: bb.h
2489 };
2490 }
2491 }
2492};
2493var copyBoundingBox = function copyBoundingBox(bb) {
2494 return {
2495 x1: bb.x1,
2496 x2: bb.x2,
2497 w: bb.w,
2498 y1: bb.y1,
2499 y2: bb.y2,
2500 h: bb.h
2501 };
2502};
2503var clearBoundingBox = function clearBoundingBox(bb) {
2504 bb.x1 = Infinity;
2505 bb.y1 = Infinity;
2506 bb.x2 = -Infinity;
2507 bb.y2 = -Infinity;
2508 bb.w = 0;
2509 bb.h = 0;
2510};
2511var updateBoundingBox = function updateBoundingBox(bb1, bb2) {
2512 // update bb1 with bb2 bounds
2513 bb1.x1 = Math.min(bb1.x1, bb2.x1);
2514 bb1.x2 = Math.max(bb1.x2, bb2.x2);
2515 bb1.w = bb1.x2 - bb1.x1;
2516 bb1.y1 = Math.min(bb1.y1, bb2.y1);
2517 bb1.y2 = Math.max(bb1.y2, bb2.y2);
2518 bb1.h = bb1.y2 - bb1.y1;
2519};
2520var expandBoundingBoxByPoint = function expandBoundingBoxByPoint(bb, x, y) {
2521 bb.x1 = Math.min(bb.x1, x);
2522 bb.x2 = Math.max(bb.x2, x);
2523 bb.w = bb.x2 - bb.x1;
2524 bb.y1 = Math.min(bb.y1, y);
2525 bb.y2 = Math.max(bb.y2, y);
2526 bb.h = bb.y2 - bb.y1;
2527};
2528var expandBoundingBox = function expandBoundingBox(bb) {
2529 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
2530 bb.x1 -= padding;
2531 bb.x2 += padding;
2532 bb.y1 -= padding;
2533 bb.y2 += padding;
2534 bb.w = bb.x2 - bb.x1;
2535 bb.h = bb.y2 - bb.y1;
2536 return bb;
2537};
2538var expandBoundingBoxSides = function expandBoundingBoxSides(bb) {
2539 var padding = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [0];
2540 var top, right, bottom, left;
2541
2542 if (padding.length === 1) {
2543 top = right = bottom = left = padding[0];
2544 } else if (padding.length === 2) {
2545 top = bottom = padding[0];
2546 left = right = padding[1];
2547 } else if (padding.length === 4) {
2548 var _padding = _slicedToArray(padding, 4);
2549
2550 top = _padding[0];
2551 right = _padding[1];
2552 bottom = _padding[2];
2553 left = _padding[3];
2554 }
2555
2556 bb.x1 -= left;
2557 bb.x2 += right;
2558 bb.y1 -= top;
2559 bb.y2 += bottom;
2560 bb.w = bb.x2 - bb.x1;
2561 bb.h = bb.y2 - bb.y1;
2562 return bb;
2563};
2564
2565var assignBoundingBox = function assignBoundingBox(bb1, bb2) {
2566 bb1.x1 = bb2.x1;
2567 bb1.y1 = bb2.y1;
2568 bb1.x2 = bb2.x2;
2569 bb1.y2 = bb2.y2;
2570 bb1.w = bb1.x2 - bb1.x1;
2571 bb1.h = bb1.y2 - bb1.y1;
2572};
2573var boundingBoxesIntersect = function boundingBoxesIntersect(bb1, bb2) {
2574 // case: one bb to right of other
2575 if (bb1.x1 > bb2.x2) {
2576 return false;
2577 }
2578
2579 if (bb2.x1 > bb1.x2) {
2580 return false;
2581 } // case: one bb to left of other
2582
2583
2584 if (bb1.x2 < bb2.x1) {
2585 return false;
2586 }
2587
2588 if (bb2.x2 < bb1.x1) {
2589 return false;
2590 } // case: one bb above other
2591
2592
2593 if (bb1.y2 < bb2.y1) {
2594 return false;
2595 }
2596
2597 if (bb2.y2 < bb1.y1) {
2598 return false;
2599 } // case: one bb below other
2600
2601
2602 if (bb1.y1 > bb2.y2) {
2603 return false;
2604 }
2605
2606 if (bb2.y1 > bb1.y2) {
2607 return false;
2608 } // otherwise, must have some overlap
2609
2610
2611 return true;
2612};
2613var inBoundingBox = function inBoundingBox(bb, x, y) {
2614 return bb.x1 <= x && x <= bb.x2 && bb.y1 <= y && y <= bb.y2;
2615};
2616var pointInBoundingBox = function pointInBoundingBox(bb, pt) {
2617 return inBoundingBox(bb, pt.x, pt.y);
2618};
2619var boundingBoxInBoundingBox = function boundingBoxInBoundingBox(bb1, bb2) {
2620 return inBoundingBox(bb1, bb2.x1, bb2.y1) && inBoundingBox(bb1, bb2.x2, bb2.y2);
2621};
2622var roundRectangleIntersectLine = function roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding) {
2623 var cornerRadius = getRoundRectangleRadius(width, height);
2624 var halfWidth = width / 2;
2625 var halfHeight = height / 2; // Check intersections with straight line segments
2626
2627 var straightLineIntersections; // Top segment, left to right
2628
2629 {
2630 var topStartX = nodeX - halfWidth + cornerRadius - padding;
2631 var topStartY = nodeY - halfHeight - padding;
2632 var topEndX = nodeX + halfWidth - cornerRadius + padding;
2633 var topEndY = topStartY;
2634 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
2635
2636 if (straightLineIntersections.length > 0) {
2637 return straightLineIntersections;
2638 }
2639 } // Right segment, top to bottom
2640
2641 {
2642 var rightStartX = nodeX + halfWidth + padding;
2643 var rightStartY = nodeY - halfHeight + cornerRadius - padding;
2644 var rightEndX = rightStartX;
2645 var rightEndY = nodeY + halfHeight - cornerRadius + padding;
2646 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, rightStartX, rightStartY, rightEndX, rightEndY, false);
2647
2648 if (straightLineIntersections.length > 0) {
2649 return straightLineIntersections;
2650 }
2651 } // Bottom segment, left to right
2652
2653 {
2654 var bottomStartX = nodeX - halfWidth + cornerRadius - padding;
2655 var bottomStartY = nodeY + halfHeight + padding;
2656 var bottomEndX = nodeX + halfWidth - cornerRadius + padding;
2657 var bottomEndY = bottomStartY;
2658 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, bottomStartX, bottomStartY, bottomEndX, bottomEndY, false);
2659
2660 if (straightLineIntersections.length > 0) {
2661 return straightLineIntersections;
2662 }
2663 } // Left segment, top to bottom
2664
2665 {
2666 var leftStartX = nodeX - halfWidth - padding;
2667 var leftStartY = nodeY - halfHeight + cornerRadius - padding;
2668 var leftEndX = leftStartX;
2669 var leftEndY = nodeY + halfHeight - cornerRadius + padding;
2670 straightLineIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, leftStartX, leftStartY, leftEndX, leftEndY, false);
2671
2672 if (straightLineIntersections.length > 0) {
2673 return straightLineIntersections;
2674 }
2675 } // Check intersections with arc segments
2676
2677 var arcIntersections; // Top Left
2678
2679 {
2680 var topLeftCenterX = nodeX - halfWidth + cornerRadius;
2681 var topLeftCenterY = nodeY - halfHeight + cornerRadius;
2682 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topLeftCenterX, topLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2683
2684 if (arcIntersections.length > 0 && arcIntersections[0] <= topLeftCenterX && arcIntersections[1] <= topLeftCenterY) {
2685 return [arcIntersections[0], arcIntersections[1]];
2686 }
2687 } // Top Right
2688
2689 {
2690 var topRightCenterX = nodeX + halfWidth - cornerRadius;
2691 var topRightCenterY = nodeY - halfHeight + cornerRadius;
2692 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, topRightCenterX, topRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2693
2694 if (arcIntersections.length > 0 && arcIntersections[0] >= topRightCenterX && arcIntersections[1] <= topRightCenterY) {
2695 return [arcIntersections[0], arcIntersections[1]];
2696 }
2697 } // Bottom Right
2698
2699 {
2700 var bottomRightCenterX = nodeX + halfWidth - cornerRadius;
2701 var bottomRightCenterY = nodeY + halfHeight - cornerRadius;
2702 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomRightCenterX, bottomRightCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2703
2704 if (arcIntersections.length > 0 && arcIntersections[0] >= bottomRightCenterX && arcIntersections[1] >= bottomRightCenterY) {
2705 return [arcIntersections[0], arcIntersections[1]];
2706 }
2707 } // Bottom Left
2708
2709 {
2710 var bottomLeftCenterX = nodeX - halfWidth + cornerRadius;
2711 var bottomLeftCenterY = nodeY + halfHeight - cornerRadius;
2712 arcIntersections = intersectLineCircle(x, y, nodeX, nodeY, bottomLeftCenterX, bottomLeftCenterY, cornerRadius + padding); // Ensure the intersection is on the desired quarter of the circle
2713
2714 if (arcIntersections.length > 0 && arcIntersections[0] <= bottomLeftCenterX && arcIntersections[1] >= bottomLeftCenterY) {
2715 return [arcIntersections[0], arcIntersections[1]];
2716 }
2717 }
2718 return []; // if nothing
2719};
2720var inLineVicinity = function inLineVicinity(x, y, lx1, ly1, lx2, ly2, tolerance) {
2721 var t = tolerance;
2722 var x1 = Math.min(lx1, lx2);
2723 var x2 = Math.max(lx1, lx2);
2724 var y1 = Math.min(ly1, ly2);
2725 var y2 = Math.max(ly1, ly2);
2726 return x1 - t <= x && x <= x2 + t && y1 - t <= y && y <= y2 + t;
2727};
2728var inBezierVicinity = function inBezierVicinity(x, y, x1, y1, x2, y2, x3, y3, tolerance) {
2729 var bb = {
2730 x1: Math.min(x1, x3, x2) - tolerance,
2731 x2: Math.max(x1, x3, x2) + tolerance,
2732 y1: Math.min(y1, y3, y2) - tolerance,
2733 y2: Math.max(y1, y3, y2) + tolerance
2734 }; // if outside the rough bounding box for the bezier, then it can't be a hit
2735
2736 if (x < bb.x1 || x > bb.x2 || y < bb.y1 || y > bb.y2) {
2737 // console.log('bezier out of rough bb')
2738 return false;
2739 } else {
2740 // console.log('do more expensive check');
2741 return true;
2742 }
2743};
2744var solveQuadratic = function solveQuadratic(a, b, c, val) {
2745 c -= val;
2746 var r = b * b - 4 * a * c;
2747
2748 if (r < 0) {
2749 return [];
2750 }
2751
2752 var sqrtR = Math.sqrt(r);
2753 var denom = 2 * a;
2754 var root1 = (-b + sqrtR) / denom;
2755 var root2 = (-b - sqrtR) / denom;
2756 return [root1, root2];
2757};
2758var solveCubic = function solveCubic(a, b, c, d, result) {
2759 // Solves a cubic function, returns root in form [r1, i1, r2, i2, r3, i3], where
2760 // r is the real component, i is the imaginary component
2761 // An implementation of the Cardano method from the year 1545
2762 // http://en.wikipedia.org/wiki/Cubic_function#The_nature_of_the_roots
2763 var epsilon = 0.00001; // avoid division by zero while keeping the overall expression close in value
2764
2765 if (a === 0) {
2766 a = epsilon;
2767 }
2768
2769 b /= a;
2770 c /= a;
2771 d /= a;
2772 var discriminant, q, r, dum1, s, t, term1, r13;
2773 q = (3.0 * c - b * b) / 9.0;
2774 r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
2775 r /= 54.0;
2776 discriminant = q * q * q + r * r;
2777 result[1] = 0;
2778 term1 = b / 3.0;
2779
2780 if (discriminant > 0) {
2781 s = r + Math.sqrt(discriminant);
2782 s = s < 0 ? -Math.pow(-s, 1.0 / 3.0) : Math.pow(s, 1.0 / 3.0);
2783 t = r - Math.sqrt(discriminant);
2784 t = t < 0 ? -Math.pow(-t, 1.0 / 3.0) : Math.pow(t, 1.0 / 3.0);
2785 result[0] = -term1 + s + t;
2786 term1 += (s + t) / 2.0;
2787 result[4] = result[2] = -term1;
2788 term1 = Math.sqrt(3.0) * (-t + s) / 2;
2789 result[3] = term1;
2790 result[5] = -term1;
2791 return;
2792 }
2793
2794 result[5] = result[3] = 0;
2795
2796 if (discriminant === 0) {
2797 r13 = r < 0 ? -Math.pow(-r, 1.0 / 3.0) : Math.pow(r, 1.0 / 3.0);
2798 result[0] = -term1 + 2.0 * r13;
2799 result[4] = result[2] = -(r13 + term1);
2800 return;
2801 }
2802
2803 q = -q;
2804 dum1 = q * q * q;
2805 dum1 = Math.acos(r / Math.sqrt(dum1));
2806 r13 = 2.0 * Math.sqrt(q);
2807 result[0] = -term1 + r13 * Math.cos(dum1 / 3.0);
2808 result[2] = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
2809 result[4] = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);
2810 return;
2811};
2812var sqdistToQuadraticBezier = function sqdistToQuadraticBezier(x, y, x1, y1, x2, y2, x3, y3) {
2813 // Find minimum distance by using the minimum of the distance
2814 // function between the given point and the curve
2815 // This gives the coefficients of the resulting cubic equation
2816 // whose roots tell us where a possible minimum is
2817 // (Coefficients are divided by 4)
2818 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;
2819 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;
2820 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;
2821 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);
2822
2823 var roots = []; // Use the cubic solving algorithm
2824
2825 solveCubic(a, b, c, d, roots);
2826 var zeroThreshold = 0.0000001;
2827 var params = [];
2828
2829 for (var index = 0; index < 6; index += 2) {
2830 if (Math.abs(roots[index + 1]) < zeroThreshold && roots[index] >= 0 && roots[index] <= 1.0) {
2831 params.push(roots[index]);
2832 }
2833 }
2834
2835 params.push(1.0);
2836 params.push(0.0);
2837 var minDistanceSquared = -1;
2838 var curX, curY, distSquared;
2839
2840 for (var i = 0; i < params.length; i++) {
2841 curX = Math.pow(1.0 - params[i], 2.0) * x1 + 2.0 * (1 - params[i]) * params[i] * x2 + params[i] * params[i] * x3;
2842 curY = Math.pow(1 - params[i], 2.0) * y1 + 2 * (1.0 - params[i]) * params[i] * y2 + params[i] * params[i] * y3;
2843 distSquared = Math.pow(curX - x, 2) + Math.pow(curY - y, 2); // debug('distance for param ' + params[i] + ": " + Math.sqrt(distSquared));
2844
2845 if (minDistanceSquared >= 0) {
2846 if (distSquared < minDistanceSquared) {
2847 minDistanceSquared = distSquared;
2848 }
2849 } else {
2850 minDistanceSquared = distSquared;
2851 }
2852 }
2853
2854 return minDistanceSquared;
2855};
2856var sqdistToFiniteLine = function sqdistToFiniteLine(x, y, x1, y1, x2, y2) {
2857 var offset = [x - x1, y - y1];
2858 var line = [x2 - x1, y2 - y1];
2859 var lineSq = line[0] * line[0] + line[1] * line[1];
2860 var hypSq = offset[0] * offset[0] + offset[1] * offset[1];
2861 var dotProduct = offset[0] * line[0] + offset[1] * line[1];
2862 var adjSq = dotProduct * dotProduct / lineSq;
2863
2864 if (dotProduct < 0) {
2865 return hypSq;
2866 }
2867
2868 if (adjSq > lineSq) {
2869 return (x - x2) * (x - x2) + (y - y2) * (y - y2);
2870 }
2871
2872 return hypSq - adjSq;
2873};
2874var pointInsidePolygonPoints = function pointInsidePolygonPoints(x, y, points) {
2875 var x1, y1, x2, y2;
2876 var y3; // Intersect with vertical line through (x, y)
2877
2878 var up = 0; // let down = 0;
2879
2880 for (var i = 0; i < points.length / 2; i++) {
2881 x1 = points[i * 2];
2882 y1 = points[i * 2 + 1];
2883
2884 if (i + 1 < points.length / 2) {
2885 x2 = points[(i + 1) * 2];
2886 y2 = points[(i + 1) * 2 + 1];
2887 } else {
2888 x2 = points[(i + 1 - points.length / 2) * 2];
2889 y2 = points[(i + 1 - points.length / 2) * 2 + 1];
2890 }
2891
2892 if (x1 == x && x2 == x) ; else if (x1 >= x && x >= x2 || x1 <= x && x <= x2) {
2893 y3 = (x - x1) / (x2 - x1) * (y2 - y1) + y1;
2894
2895 if (y3 > y) {
2896 up++;
2897 } // if( y3 < y ){
2898 // down++;
2899 // }
2900
2901 } else {
2902 continue;
2903 }
2904 }
2905
2906 if (up % 2 === 0) {
2907 return false;
2908 } else {
2909 return true;
2910 }
2911};
2912var pointInsidePolygon = function pointInsidePolygon(x, y, basePoints, centerX, centerY, width, height, direction, padding) {
2913 var transformedPoints = new Array(basePoints.length); // Gives negative angle
2914
2915 var angle;
2916
2917 if (direction[0] != null) {
2918 angle = Math.atan(direction[1] / direction[0]);
2919
2920 if (direction[0] < 0) {
2921 angle = angle + Math.PI / 2;
2922 } else {
2923 angle = -angle - Math.PI / 2;
2924 }
2925 } else {
2926 angle = direction;
2927 }
2928
2929 var cos = Math.cos(-angle);
2930 var sin = Math.sin(-angle); // console.log("base: " + basePoints);
2931
2932 for (var i = 0; i < transformedPoints.length / 2; i++) {
2933 transformedPoints[i * 2] = width / 2 * (basePoints[i * 2] * cos - basePoints[i * 2 + 1] * sin);
2934 transformedPoints[i * 2 + 1] = height / 2 * (basePoints[i * 2 + 1] * cos + basePoints[i * 2] * sin);
2935 transformedPoints[i * 2] += centerX;
2936 transformedPoints[i * 2 + 1] += centerY;
2937 }
2938
2939 var points;
2940
2941 if (padding > 0) {
2942 var expandedLineSet = expandPolygon(transformedPoints, -padding);
2943 points = joinLines(expandedLineSet);
2944 } else {
2945 points = transformedPoints;
2946 }
2947
2948 return pointInsidePolygonPoints(x, y, points);
2949};
2950var pointInsideRoundPolygon = function pointInsideRoundPolygon(x, y, basePoints, centerX, centerY, width, height) {
2951 var cutPolygonPoints = new Array(basePoints.length);
2952 var halfW = width / 2;
2953 var halfH = height / 2;
2954 var cornerRadius = getRoundPolygonRadius(width, height);
2955 var squaredCornerRadius = cornerRadius * cornerRadius;
2956
2957 for (var i = 0; i < basePoints.length / 4; i++) {
2958 var sourceUv = void 0,
2959 destUv = void 0;
2960
2961 if (i === 0) {
2962 sourceUv = basePoints.length - 2;
2963 } else {
2964 sourceUv = i * 4 - 2;
2965 }
2966
2967 destUv = i * 4 + 2;
2968 var px = centerX + halfW * basePoints[i * 4];
2969 var py = centerY + halfH * basePoints[i * 4 + 1];
2970 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
2971 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
2972 var cp0x = px - offset * basePoints[sourceUv];
2973 var cp0y = py - offset * basePoints[sourceUv + 1];
2974 var cp1x = px + offset * basePoints[destUv];
2975 var cp1y = py + offset * basePoints[destUv + 1];
2976 cutPolygonPoints[i * 4] = cp0x;
2977 cutPolygonPoints[i * 4 + 1] = cp0y;
2978 cutPolygonPoints[i * 4 + 2] = cp1x;
2979 cutPolygonPoints[i * 4 + 3] = cp1y;
2980 var orthx = basePoints[sourceUv + 1];
2981 var orthy = -basePoints[sourceUv];
2982 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
2983
2984 if (cosAlpha < 0) {
2985 orthx *= -1;
2986 orthy *= -1;
2987 }
2988
2989 var cx = cp0x + orthx * cornerRadius;
2990 var cy = cp0y + orthy * cornerRadius;
2991 var squaredDistance = Math.pow(cx - x, 2) + Math.pow(cy - y, 2);
2992
2993 if (squaredDistance <= squaredCornerRadius) {
2994 return true;
2995 }
2996 }
2997
2998 return pointInsidePolygonPoints(x, y, cutPolygonPoints);
2999};
3000var joinLines = function joinLines(lineSet) {
3001 var vertices = new Array(lineSet.length / 2);
3002 var currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY;
3003 var nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY;
3004
3005 for (var i = 0; i < lineSet.length / 4; i++) {
3006 currentLineStartX = lineSet[i * 4];
3007 currentLineStartY = lineSet[i * 4 + 1];
3008 currentLineEndX = lineSet[i * 4 + 2];
3009 currentLineEndY = lineSet[i * 4 + 3];
3010
3011 if (i < lineSet.length / 4 - 1) {
3012 nextLineStartX = lineSet[(i + 1) * 4];
3013 nextLineStartY = lineSet[(i + 1) * 4 + 1];
3014 nextLineEndX = lineSet[(i + 1) * 4 + 2];
3015 nextLineEndY = lineSet[(i + 1) * 4 + 3];
3016 } else {
3017 nextLineStartX = lineSet[0];
3018 nextLineStartY = lineSet[1];
3019 nextLineEndX = lineSet[2];
3020 nextLineEndY = lineSet[3];
3021 }
3022
3023 var intersection = finiteLinesIntersect(currentLineStartX, currentLineStartY, currentLineEndX, currentLineEndY, nextLineStartX, nextLineStartY, nextLineEndX, nextLineEndY, true);
3024 vertices[i * 2] = intersection[0];
3025 vertices[i * 2 + 1] = intersection[1];
3026 }
3027
3028 return vertices;
3029};
3030var expandPolygon = function expandPolygon(points, pad) {
3031 var expandedLineSet = new Array(points.length * 2);
3032 var currentPointX, currentPointY, nextPointX, nextPointY;
3033
3034 for (var i = 0; i < points.length / 2; i++) {
3035 currentPointX = points[i * 2];
3036 currentPointY = points[i * 2 + 1];
3037
3038 if (i < points.length / 2 - 1) {
3039 nextPointX = points[(i + 1) * 2];
3040 nextPointY = points[(i + 1) * 2 + 1];
3041 } else {
3042 nextPointX = points[0];
3043 nextPointY = points[1];
3044 } // Current line: [currentPointX, currentPointY] to [nextPointX, nextPointY]
3045 // Assume CCW polygon winding
3046
3047
3048 var offsetX = nextPointY - currentPointY;
3049 var offsetY = -(nextPointX - currentPointX); // Normalize
3050
3051 var offsetLength = Math.sqrt(offsetX * offsetX + offsetY * offsetY);
3052 var normalizedOffsetX = offsetX / offsetLength;
3053 var normalizedOffsetY = offsetY / offsetLength;
3054 expandedLineSet[i * 4] = currentPointX + normalizedOffsetX * pad;
3055 expandedLineSet[i * 4 + 1] = currentPointY + normalizedOffsetY * pad;
3056 expandedLineSet[i * 4 + 2] = nextPointX + normalizedOffsetX * pad;
3057 expandedLineSet[i * 4 + 3] = nextPointY + normalizedOffsetY * pad;
3058 }
3059
3060 return expandedLineSet;
3061};
3062var intersectLineEllipse = function intersectLineEllipse(x, y, centerX, centerY, ellipseWradius, ellipseHradius) {
3063 var dispX = centerX - x;
3064 var dispY = centerY - y;
3065 dispX /= ellipseWradius;
3066 dispY /= ellipseHradius;
3067 var len = Math.sqrt(dispX * dispX + dispY * dispY);
3068 var newLength = len - 1;
3069
3070 if (newLength < 0) {
3071 return [];
3072 }
3073
3074 var lenProportion = newLength / len;
3075 return [(centerX - x) * lenProportion + x, (centerY - y) * lenProportion + y];
3076};
3077var checkInEllipse = function checkInEllipse(x, y, width, height, centerX, centerY, padding) {
3078 x -= centerX;
3079 y -= centerY;
3080 x /= width / 2 + padding;
3081 y /= height / 2 + padding;
3082 return x * x + y * y <= 1;
3083}; // Returns intersections of increasing distance from line's start point
3084
3085var intersectLineCircle = function intersectLineCircle(x1, y1, x2, y2, centerX, centerY, radius) {
3086 // Calculate d, direction vector of line
3087 var d = [x2 - x1, y2 - y1]; // Direction vector of line
3088
3089 var f = [x1 - centerX, y1 - centerY];
3090 var a = d[0] * d[0] + d[1] * d[1];
3091 var b = 2 * (f[0] * d[0] + f[1] * d[1]);
3092 var c = f[0] * f[0] + f[1] * f[1] - radius * radius;
3093 var discriminant = b * b - 4 * a * c;
3094
3095 if (discriminant < 0) {
3096 return [];
3097 }
3098
3099 var t1 = (-b + Math.sqrt(discriminant)) / (2 * a);
3100 var t2 = (-b - Math.sqrt(discriminant)) / (2 * a);
3101 var tMin = Math.min(t1, t2);
3102 var tMax = Math.max(t1, t2);
3103 var inRangeParams = [];
3104
3105 if (tMin >= 0 && tMin <= 1) {
3106 inRangeParams.push(tMin);
3107 }
3108
3109 if (tMax >= 0 && tMax <= 1) {
3110 inRangeParams.push(tMax);
3111 }
3112
3113 if (inRangeParams.length === 0) {
3114 return [];
3115 }
3116
3117 var nearIntersectionX = inRangeParams[0] * d[0] + x1;
3118 var nearIntersectionY = inRangeParams[0] * d[1] + y1;
3119
3120 if (inRangeParams.length > 1) {
3121 if (inRangeParams[0] == inRangeParams[1]) {
3122 return [nearIntersectionX, nearIntersectionY];
3123 } else {
3124 var farIntersectionX = inRangeParams[1] * d[0] + x1;
3125 var farIntersectionY = inRangeParams[1] * d[1] + y1;
3126 return [nearIntersectionX, nearIntersectionY, farIntersectionX, farIntersectionY];
3127 }
3128 } else {
3129 return [nearIntersectionX, nearIntersectionY];
3130 }
3131};
3132var midOfThree = function midOfThree(a, b, c) {
3133 if (b <= a && a <= c || c <= a && a <= b) {
3134 return a;
3135 } else if (a <= b && b <= c || c <= b && b <= a) {
3136 return b;
3137 } else {
3138 return c;
3139 }
3140}; // (x1,y1)=>(x2,y2) intersect with (x3,y3)=>(x4,y4)
3141
3142var finiteLinesIntersect = function finiteLinesIntersect(x1, y1, x2, y2, x3, y3, x4, y4, infiniteLines) {
3143 var dx13 = x1 - x3;
3144 var dx21 = x2 - x1;
3145 var dx43 = x4 - x3;
3146 var dy13 = y1 - y3;
3147 var dy21 = y2 - y1;
3148 var dy43 = y4 - y3;
3149 var ua_t = dx43 * dy13 - dy43 * dx13;
3150 var ub_t = dx21 * dy13 - dy21 * dx13;
3151 var u_b = dy43 * dx21 - dx43 * dy21;
3152
3153 if (u_b !== 0) {
3154 var ua = ua_t / u_b;
3155 var ub = ub_t / u_b;
3156 var flptThreshold = 0.001;
3157
3158 var _min = 0 - flptThreshold;
3159
3160 var _max = 1 + flptThreshold;
3161
3162 if (_min <= ua && ua <= _max && _min <= ub && ub <= _max) {
3163 return [x1 + ua * dx21, y1 + ua * dy21];
3164 } else {
3165 if (!infiniteLines) {
3166 return [];
3167 } else {
3168 return [x1 + ua * dx21, y1 + ua * dy21];
3169 }
3170 }
3171 } else {
3172 if (ua_t === 0 || ub_t === 0) {
3173 // Parallel, coincident lines. Check if overlap
3174 // Check endpoint of second line
3175 if (midOfThree(x1, x2, x4) === x4) {
3176 return [x4, y4];
3177 } // Check start point of second line
3178
3179
3180 if (midOfThree(x1, x2, x3) === x3) {
3181 return [x3, y3];
3182 } // Endpoint of first line
3183
3184
3185 if (midOfThree(x3, x4, x2) === x2) {
3186 return [x2, y2];
3187 }
3188
3189 return [];
3190 } else {
3191 // Parallel, non-coincident
3192 return [];
3193 }
3194 }
3195}; // math.polygonIntersectLine( x, y, basePoints, centerX, centerY, width, height, padding )
3196// intersect a node polygon (pts transformed)
3197//
3198// math.polygonIntersectLine( x, y, basePoints, centerX, centerY )
3199// intersect the points (no transform)
3200
3201var polygonIntersectLine = function polygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3202 var intersections = [];
3203 var intersection;
3204 var transformedPoints = new Array(basePoints.length);
3205 var doTransform = true;
3206
3207 if (width == null) {
3208 doTransform = false;
3209 }
3210
3211 var points;
3212
3213 if (doTransform) {
3214 for (var i = 0; i < transformedPoints.length / 2; i++) {
3215 transformedPoints[i * 2] = basePoints[i * 2] * width + centerX;
3216 transformedPoints[i * 2 + 1] = basePoints[i * 2 + 1] * height + centerY;
3217 }
3218
3219 if (padding > 0) {
3220 var expandedLineSet = expandPolygon(transformedPoints, -padding);
3221 points = joinLines(expandedLineSet);
3222 } else {
3223 points = transformedPoints;
3224 }
3225 } else {
3226 points = basePoints;
3227 }
3228
3229 var currentX, currentY, nextX, nextY;
3230
3231 for (var _i2 = 0; _i2 < points.length / 2; _i2++) {
3232 currentX = points[_i2 * 2];
3233 currentY = points[_i2 * 2 + 1];
3234
3235 if (_i2 < points.length / 2 - 1) {
3236 nextX = points[(_i2 + 1) * 2];
3237 nextY = points[(_i2 + 1) * 2 + 1];
3238 } else {
3239 nextX = points[0];
3240 nextY = points[1];
3241 }
3242
3243 intersection = finiteLinesIntersect(x, y, centerX, centerY, currentX, currentY, nextX, nextY);
3244
3245 if (intersection.length !== 0) {
3246 intersections.push(intersection[0], intersection[1]);
3247 }
3248 }
3249
3250 return intersections;
3251};
3252var roundPolygonIntersectLine = function roundPolygonIntersectLine(x, y, basePoints, centerX, centerY, width, height, padding) {
3253 var intersections = [];
3254 var intersection;
3255 var lines = new Array(basePoints.length);
3256 var halfW = width / 2;
3257 var halfH = height / 2;
3258 var cornerRadius = getRoundPolygonRadius(width, height);
3259
3260 for (var i = 0; i < basePoints.length / 4; i++) {
3261 var sourceUv = void 0,
3262 destUv = void 0;
3263
3264 if (i === 0) {
3265 sourceUv = basePoints.length - 2;
3266 } else {
3267 sourceUv = i * 4 - 2;
3268 }
3269
3270 destUv = i * 4 + 2;
3271 var px = centerX + halfW * basePoints[i * 4];
3272 var py = centerY + halfH * basePoints[i * 4 + 1];
3273 var cosTheta = -basePoints[sourceUv] * basePoints[destUv] - basePoints[sourceUv + 1] * basePoints[destUv + 1];
3274 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
3275 var cp0x = px - offset * basePoints[sourceUv];
3276 var cp0y = py - offset * basePoints[sourceUv + 1];
3277 var cp1x = px + offset * basePoints[destUv];
3278 var cp1y = py + offset * basePoints[destUv + 1];
3279
3280 if (i === 0) {
3281 lines[basePoints.length - 2] = cp0x;
3282 lines[basePoints.length - 1] = cp0y;
3283 } else {
3284 lines[i * 4 - 2] = cp0x;
3285 lines[i * 4 - 1] = cp0y;
3286 }
3287
3288 lines[i * 4] = cp1x;
3289 lines[i * 4 + 1] = cp1y;
3290 var orthx = basePoints[sourceUv + 1];
3291 var orthy = -basePoints[sourceUv];
3292 var cosAlpha = orthx * basePoints[destUv] + orthy * basePoints[destUv + 1];
3293
3294 if (cosAlpha < 0) {
3295 orthx *= -1;
3296 orthy *= -1;
3297 }
3298
3299 var cx = cp0x + orthx * cornerRadius;
3300 var cy = cp0y + orthy * cornerRadius;
3301 intersection = intersectLineCircle(x, y, centerX, centerY, cx, cy, cornerRadius);
3302
3303 if (intersection.length !== 0) {
3304 intersections.push(intersection[0], intersection[1]);
3305 }
3306 }
3307
3308 for (var _i3 = 0; _i3 < lines.length / 4; _i3++) {
3309 intersection = finiteLinesIntersect(x, y, centerX, centerY, lines[_i3 * 4], lines[_i3 * 4 + 1], lines[_i3 * 4 + 2], lines[_i3 * 4 + 3], false);
3310
3311 if (intersection.length !== 0) {
3312 intersections.push(intersection[0], intersection[1]);
3313 }
3314 }
3315
3316 if (intersections.length > 2) {
3317 var lowestIntersection = [intersections[0], intersections[1]];
3318 var lowestSquaredDistance = Math.pow(lowestIntersection[0] - x, 2) + Math.pow(lowestIntersection[1] - y, 2);
3319
3320 for (var _i4 = 1; _i4 < intersections.length / 2; _i4++) {
3321 var squaredDistance = Math.pow(intersections[_i4 * 2] - x, 2) + Math.pow(intersections[_i4 * 2 + 1] - y, 2);
3322
3323 if (squaredDistance <= lowestSquaredDistance) {
3324 lowestIntersection[0] = intersections[_i4 * 2];
3325 lowestIntersection[1] = intersections[_i4 * 2 + 1];
3326 lowestSquaredDistance = squaredDistance;
3327 }
3328 }
3329
3330 return lowestIntersection;
3331 }
3332
3333 return intersections;
3334};
3335var shortenIntersection = function shortenIntersection(intersection, offset, amount) {
3336 var disp = [intersection[0] - offset[0], intersection[1] - offset[1]];
3337 var length = Math.sqrt(disp[0] * disp[0] + disp[1] * disp[1]);
3338 var lenRatio = (length - amount) / length;
3339
3340 if (lenRatio < 0) {
3341 lenRatio = 0.00001;
3342 }
3343
3344 return [offset[0] + lenRatio * disp[0], offset[1] + lenRatio * disp[1]];
3345};
3346var generateUnitNgonPointsFitToSquare = function generateUnitNgonPointsFitToSquare(sides, rotationRadians) {
3347 var points = generateUnitNgonPoints(sides, rotationRadians);
3348 points = fitPolygonToSquare(points);
3349 return points;
3350};
3351var fitPolygonToSquare = function fitPolygonToSquare(points) {
3352 var x, y;
3353 var sides = points.length / 2;
3354 var minX = Infinity,
3355 minY = Infinity,
3356 maxX = -Infinity,
3357 maxY = -Infinity;
3358
3359 for (var i = 0; i < sides; i++) {
3360 x = points[2 * i];
3361 y = points[2 * i + 1];
3362 minX = Math.min(minX, x);
3363 maxX = Math.max(maxX, x);
3364 minY = Math.min(minY, y);
3365 maxY = Math.max(maxY, y);
3366 } // stretch factors
3367
3368
3369 var sx = 2 / (maxX - minX);
3370 var sy = 2 / (maxY - minY);
3371
3372 for (var _i5 = 0; _i5 < sides; _i5++) {
3373 x = points[2 * _i5] = points[2 * _i5] * sx;
3374 y = points[2 * _i5 + 1] = points[2 * _i5 + 1] * sy;
3375 minX = Math.min(minX, x);
3376 maxX = Math.max(maxX, x);
3377 minY = Math.min(minY, y);
3378 maxY = Math.max(maxY, y);
3379 }
3380
3381 if (minY < -1) {
3382 for (var _i6 = 0; _i6 < sides; _i6++) {
3383 y = points[2 * _i6 + 1] = points[2 * _i6 + 1] + (-1 - minY);
3384 }
3385 }
3386
3387 return points;
3388};
3389var generateUnitNgonPoints = function generateUnitNgonPoints(sides, rotationRadians) {
3390 var increment = 1.0 / sides * 2 * Math.PI;
3391 var startAngle = sides % 2 === 0 ? Math.PI / 2.0 + increment / 2.0 : Math.PI / 2.0;
3392 startAngle += rotationRadians;
3393 var points = new Array(sides * 2);
3394 var currentAngle;
3395
3396 for (var i = 0; i < sides; i++) {
3397 currentAngle = i * increment + startAngle;
3398 points[2 * i] = Math.cos(currentAngle); // x
3399
3400 points[2 * i + 1] = Math.sin(-currentAngle); // y
3401 }
3402
3403 return points;
3404}; // Set the default radius, unless half of width or height is smaller than default
3405
3406var getRoundRectangleRadius = function getRoundRectangleRadius(width, height) {
3407 return Math.min(width / 4, height / 4, 8);
3408}; // Set the default radius
3409
3410var getRoundPolygonRadius = function getRoundPolygonRadius(width, height) {
3411 return Math.min(width / 10, height / 10, 8);
3412};
3413var getCutRectangleCornerLength = function getCutRectangleCornerLength() {
3414 return 8;
3415};
3416var bezierPtsToQuadCoeff = function bezierPtsToQuadCoeff(p0, p1, p2) {
3417 return [p0 - 2 * p1 + p2, 2 * (p1 - p0), p0];
3418}; // get curve width, height, and control point position offsets as a percentage of node height / width
3419
3420var getBarrelCurveConstants = function getBarrelCurveConstants(width, height) {
3421 return {
3422 heightOffset: Math.min(15, 0.05 * height),
3423 widthOffset: Math.min(100, 0.25 * width),
3424 ctrlPtOffsetPct: 0.05
3425 };
3426};
3427
3428var pageRankDefaults = defaults({
3429 dampingFactor: 0.8,
3430 precision: 0.000001,
3431 iterations: 200,
3432 weight: function weight(edge) {
3433 return 1;
3434 }
3435});
3436var elesfn$7 = {
3437 pageRank: function pageRank(options) {
3438 var _pageRankDefaults = pageRankDefaults(options),
3439 dampingFactor = _pageRankDefaults.dampingFactor,
3440 precision = _pageRankDefaults.precision,
3441 iterations = _pageRankDefaults.iterations,
3442 weight = _pageRankDefaults.weight;
3443
3444 var cy = this._private.cy;
3445
3446 var _this$byGroup = this.byGroup(),
3447 nodes = _this$byGroup.nodes,
3448 edges = _this$byGroup.edges;
3449
3450 var numNodes = nodes.length;
3451 var numNodesSqd = numNodes * numNodes;
3452 var numEdges = edges.length; // Construct transposed adjacency matrix
3453 // First lets have a zeroed matrix of the right size
3454 // We'll also keep track of the sum of each column
3455
3456 var matrix = new Array(numNodesSqd);
3457 var columnSum = new Array(numNodes);
3458 var additionalProb = (1 - dampingFactor) / numNodes; // Create null matrix
3459
3460 for (var i = 0; i < numNodes; i++) {
3461 for (var j = 0; j < numNodes; j++) {
3462 var n = i * numNodes + j;
3463 matrix[n] = 0;
3464 }
3465
3466 columnSum[i] = 0;
3467 } // Now, process edges
3468
3469
3470 for (var _i = 0; _i < numEdges; _i++) {
3471 var edge = edges[_i];
3472 var srcId = edge.data('source');
3473 var tgtId = edge.data('target'); // Don't include loops in the matrix
3474
3475 if (srcId === tgtId) {
3476 continue;
3477 }
3478
3479 var s = nodes.indexOfId(srcId);
3480 var t = nodes.indexOfId(tgtId);
3481 var w = weight(edge);
3482
3483 var _n = t * numNodes + s; // Update matrix
3484
3485
3486 matrix[_n] += w; // Update column sum
3487
3488 columnSum[s] += w;
3489 } // Add additional probability based on damping factor
3490 // Also, take into account columns that have sum = 0
3491
3492
3493 var p = 1.0 / numNodes + additionalProb; // Shorthand
3494 // Traverse matrix, column by column
3495
3496 for (var _j = 0; _j < numNodes; _j++) {
3497 if (columnSum[_j] === 0) {
3498 // No 'links' out from node jth, assume equal probability for each possible node
3499 for (var _i2 = 0; _i2 < numNodes; _i2++) {
3500 var _n2 = _i2 * numNodes + _j;
3501
3502 matrix[_n2] = p;
3503 }
3504 } else {
3505 // Node jth has outgoing link, compute normalized probabilities
3506 for (var _i3 = 0; _i3 < numNodes; _i3++) {
3507 var _n3 = _i3 * numNodes + _j;
3508
3509 matrix[_n3] = matrix[_n3] / columnSum[_j] + additionalProb;
3510 }
3511 }
3512 } // Compute dominant eigenvector using power method
3513
3514
3515 var eigenvector = new Array(numNodes);
3516 var temp = new Array(numNodes);
3517 var previous; // Start with a vector of all 1's
3518 // Also, initialize a null vector which will be used as shorthand
3519
3520 for (var _i4 = 0; _i4 < numNodes; _i4++) {
3521 eigenvector[_i4] = 1;
3522 }
3523
3524 for (var iter = 0; iter < iterations; iter++) {
3525 // Temp array with all 0's
3526 for (var _i5 = 0; _i5 < numNodes; _i5++) {
3527 temp[_i5] = 0;
3528 } // Multiply matrix with previous result
3529
3530
3531 for (var _i6 = 0; _i6 < numNodes; _i6++) {
3532 for (var _j2 = 0; _j2 < numNodes; _j2++) {
3533 var _n4 = _i6 * numNodes + _j2;
3534
3535 temp[_i6] += matrix[_n4] * eigenvector[_j2];
3536 }
3537 }
3538
3539 inPlaceSumNormalize(temp);
3540 previous = eigenvector;
3541 eigenvector = temp;
3542 temp = previous;
3543 var diff = 0; // Compute difference (squared module) of both vectors
3544
3545 for (var _i7 = 0; _i7 < numNodes; _i7++) {
3546 var delta = previous[_i7] - eigenvector[_i7];
3547 diff += delta * delta;
3548 } // If difference is less than the desired threshold, stop iterating
3549
3550
3551 if (diff < precision) {
3552 break;
3553 }
3554 } // Construct result
3555
3556
3557 var res = {
3558 rank: function rank(node) {
3559 node = cy.collection(node)[0];
3560 return eigenvector[nodes.indexOf(node)];
3561 }
3562 };
3563 return res;
3564 } // pageRank
3565
3566}; // elesfn
3567
3568var defaults$1 = defaults({
3569 root: null,
3570 weight: function weight(edge) {
3571 return 1;
3572 },
3573 directed: false,
3574 alpha: 0
3575});
3576var elesfn$8 = {
3577 degreeCentralityNormalized: function degreeCentralityNormalized(options) {
3578 options = defaults$1(options);
3579 var cy = this.cy();
3580 var nodes = this.nodes();
3581 var numNodes = nodes.length;
3582
3583 if (!options.directed) {
3584 var degrees = {};
3585 var maxDegree = 0;
3586
3587 for (var i = 0; i < numNodes; i++) {
3588 var node = nodes[i]; // add current node to the current options object and call degreeCentrality
3589
3590 options.root = node;
3591 var currDegree = this.degreeCentrality(options);
3592
3593 if (maxDegree < currDegree.degree) {
3594 maxDegree = currDegree.degree;
3595 }
3596
3597 degrees[node.id()] = currDegree.degree;
3598 }
3599
3600 return {
3601 degree: function degree(node) {
3602 if (maxDegree === 0) {
3603 return 0;
3604 }
3605
3606 if (string(node)) {
3607 // from is a selector string
3608 node = cy.filter(node);
3609 }
3610
3611 return degrees[node.id()] / maxDegree;
3612 }
3613 };
3614 } else {
3615 var indegrees = {};
3616 var outdegrees = {};
3617 var maxIndegree = 0;
3618 var maxOutdegree = 0;
3619
3620 for (var _i = 0; _i < numNodes; _i++) {
3621 var _node = nodes[_i];
3622
3623 var id = _node.id(); // add current node to the current options object and call degreeCentrality
3624
3625
3626 options.root = _node;
3627
3628 var _currDegree = this.degreeCentrality(options);
3629
3630 if (maxIndegree < _currDegree.indegree) maxIndegree = _currDegree.indegree;
3631 if (maxOutdegree < _currDegree.outdegree) maxOutdegree = _currDegree.outdegree;
3632 indegrees[id] = _currDegree.indegree;
3633 outdegrees[id] = _currDegree.outdegree;
3634 }
3635
3636 return {
3637 indegree: function indegree(node) {
3638 if (maxIndegree == 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 indegrees[node.id()] / maxIndegree;
3648 },
3649 outdegree: function outdegree(node) {
3650 if (maxOutdegree === 0) {
3651 return 0;
3652 }
3653
3654 if (string(node)) {
3655 // from is a selector string
3656 node = cy.filter(node);
3657 }
3658
3659 return outdegrees[node.id()] / maxOutdegree;
3660 }
3661 };
3662 }
3663 },
3664 // degreeCentralityNormalized
3665 // Implemented from the algorithm in Opsahl's paper
3666 // "Node centrality in weighted networks: Generalizing degree and shortest paths"
3667 // check the heading 2 "Degree"
3668 degreeCentrality: function degreeCentrality(options) {
3669 options = defaults$1(options);
3670 var cy = this.cy();
3671 var callingEles = this;
3672 var _options = options,
3673 root = _options.root,
3674 weight = _options.weight,
3675 directed = _options.directed,
3676 alpha = _options.alpha;
3677 root = cy.collection(root)[0];
3678
3679 if (!directed) {
3680 var connEdges = root.connectedEdges().intersection(callingEles);
3681 var k = connEdges.length;
3682 var s = 0; // Now, sum edge weights
3683
3684 for (var i = 0; i < connEdges.length; i++) {
3685 s += weight(connEdges[i]);
3686 }
3687
3688 return {
3689 degree: Math.pow(k, 1 - alpha) * Math.pow(s, alpha)
3690 };
3691 } else {
3692 var edges = root.connectedEdges();
3693 var incoming = edges.filter(function (edge) {
3694 return edge.target().same(root) && callingEles.has(edge);
3695 });
3696 var outgoing = edges.filter(function (edge) {
3697 return edge.source().same(root) && callingEles.has(edge);
3698 });
3699 var k_in = incoming.length;
3700 var k_out = outgoing.length;
3701 var s_in = 0;
3702 var s_out = 0; // Now, sum incoming edge weights
3703
3704 for (var _i2 = 0; _i2 < incoming.length; _i2++) {
3705 s_in += weight(incoming[_i2]);
3706 } // Now, sum outgoing edge weights
3707
3708
3709 for (var _i3 = 0; _i3 < outgoing.length; _i3++) {
3710 s_out += weight(outgoing[_i3]);
3711 }
3712
3713 return {
3714 indegree: Math.pow(k_in, 1 - alpha) * Math.pow(s_in, alpha),
3715 outdegree: Math.pow(k_out, 1 - alpha) * Math.pow(s_out, alpha)
3716 };
3717 }
3718 } // degreeCentrality
3719
3720}; // elesfn
3721// nice, short mathemathical alias
3722
3723elesfn$8.dc = elesfn$8.degreeCentrality;
3724elesfn$8.dcn = elesfn$8.degreeCentralityNormalised = elesfn$8.degreeCentralityNormalized;
3725
3726var defaults$2 = defaults({
3727 harmonic: true,
3728 weight: function weight() {
3729 return 1;
3730 },
3731 directed: false,
3732 root: null
3733});
3734var elesfn$9 = {
3735 closenessCentralityNormalized: function closenessCentralityNormalized(options) {
3736 var _defaults = defaults$2(options),
3737 harmonic = _defaults.harmonic,
3738 weight = _defaults.weight,
3739 directed = _defaults.directed;
3740
3741 var cy = this.cy();
3742 var closenesses = {};
3743 var maxCloseness = 0;
3744 var nodes = this.nodes();
3745 var fw = this.floydWarshall({
3746 weight: weight,
3747 directed: directed
3748 }); // Compute closeness for every node and find the maximum closeness
3749
3750 for (var i = 0; i < nodes.length; i++) {
3751 var currCloseness = 0;
3752 var node_i = nodes[i];
3753
3754 for (var j = 0; j < nodes.length; j++) {
3755 if (i !== j) {
3756 var d = fw.distance(node_i, nodes[j]);
3757
3758 if (harmonic) {
3759 currCloseness += 1 / d;
3760 } else {
3761 currCloseness += d;
3762 }
3763 }
3764 }
3765
3766 if (!harmonic) {
3767 currCloseness = 1 / currCloseness;
3768 }
3769
3770 if (maxCloseness < currCloseness) {
3771 maxCloseness = currCloseness;
3772 }
3773
3774 closenesses[node_i.id()] = currCloseness;
3775 }
3776
3777 return {
3778 closeness: function closeness(node) {
3779 if (maxCloseness == 0) {
3780 return 0;
3781 }
3782
3783 if (string(node)) {
3784 // from is a selector string
3785 node = cy.filter(node)[0].id();
3786 } else {
3787 // from is a node
3788 node = node.id();
3789 }
3790
3791 return closenesses[node] / maxCloseness;
3792 }
3793 };
3794 },
3795 // Implemented from pseudocode from wikipedia
3796 closenessCentrality: function closenessCentrality(options) {
3797 var _defaults2 = defaults$2(options),
3798 root = _defaults2.root,
3799 weight = _defaults2.weight,
3800 directed = _defaults2.directed,
3801 harmonic = _defaults2.harmonic;
3802
3803 root = this.filter(root)[0]; // we need distance from this node to every other node
3804
3805 var dijkstra = this.dijkstra({
3806 root: root,
3807 weight: weight,
3808 directed: directed
3809 });
3810 var totalDistance = 0;
3811 var nodes = this.nodes();
3812
3813 for (var i = 0; i < nodes.length; i++) {
3814 var n = nodes[i];
3815
3816 if (!n.same(root)) {
3817 var d = dijkstra.distanceTo(n);
3818
3819 if (harmonic) {
3820 totalDistance += 1 / d;
3821 } else {
3822 totalDistance += d;
3823 }
3824 }
3825 }
3826
3827 return harmonic ? totalDistance : 1 / totalDistance;
3828 } // closenessCentrality
3829
3830}; // elesfn
3831// nice, short mathemathical alias
3832
3833elesfn$9.cc = elesfn$9.closenessCentrality;
3834elesfn$9.ccn = elesfn$9.closenessCentralityNormalised = elesfn$9.closenessCentralityNormalized;
3835
3836var defaults$3 = defaults({
3837 weight: null,
3838 directed: false
3839});
3840var elesfn$a = {
3841 // Implemented from the algorithm in the paper "On Variants of Shortest-Path Betweenness Centrality and their Generic Computation" by Ulrik Brandes
3842 betweennessCentrality: function betweennessCentrality(options) {
3843 var _defaults = defaults$3(options),
3844 directed = _defaults.directed,
3845 weight = _defaults.weight;
3846
3847 var weighted = weight != null;
3848 var cy = this.cy(); // starting
3849
3850 var V = this.nodes();
3851 var A = {};
3852 var _C = {};
3853 var max = 0;
3854 var C = {
3855 set: function set(key, val) {
3856 _C[key] = val;
3857
3858 if (val > max) {
3859 max = val;
3860 }
3861 },
3862 get: function get(key) {
3863 return _C[key];
3864 }
3865 }; // A contains the neighborhoods of every node
3866
3867 for (var i = 0; i < V.length; i++) {
3868 var v = V[i];
3869 var vid = v.id();
3870
3871 if (directed) {
3872 A[vid] = v.outgoers().nodes(); // get outgoers of every node
3873 } else {
3874 A[vid] = v.openNeighborhood().nodes(); // get neighbors of every node
3875 }
3876
3877 C.set(vid, 0);
3878 }
3879
3880 var _loop = function _loop(s) {
3881 var sid = V[s].id();
3882 var S = []; // stack
3883
3884 var P = {};
3885 var g = {};
3886 var d = {};
3887 var Q = new Heap(function (a, b) {
3888 return d[a] - d[b];
3889 }); // queue
3890 // init dictionaries
3891
3892 for (var _i = 0; _i < V.length; _i++) {
3893 var _vid = V[_i].id();
3894
3895 P[_vid] = [];
3896 g[_vid] = 0;
3897 d[_vid] = Infinity;
3898 }
3899
3900 g[sid] = 1; // sigma
3901
3902 d[sid] = 0; // distance to s
3903
3904 Q.push(sid);
3905
3906 while (!Q.empty()) {
3907 var _v = Q.pop();
3908
3909 S.push(_v);
3910
3911 if (weighted) {
3912 for (var j = 0; j < A[_v].length; j++) {
3913 var w = A[_v][j];
3914 var vEle = cy.getElementById(_v);
3915 var edge = void 0;
3916
3917 if (vEle.edgesTo(w).length > 0) {
3918 edge = vEle.edgesTo(w)[0];
3919 } else {
3920 edge = w.edgesTo(vEle)[0];
3921 }
3922
3923 var edgeWeight = weight(edge);
3924 w = w.id();
3925
3926 if (d[w] > d[_v] + edgeWeight) {
3927 d[w] = d[_v] + edgeWeight;
3928
3929 if (Q.nodes.indexOf(w) < 0) {
3930 //if w is not in Q
3931 Q.push(w);
3932 } else {
3933 // update position if w is in Q
3934 Q.updateItem(w);
3935 }
3936
3937 g[w] = 0;
3938 P[w] = [];
3939 }
3940
3941 if (d[w] == d[_v] + edgeWeight) {
3942 g[w] = g[w] + g[_v];
3943 P[w].push(_v);
3944 }
3945 }
3946 } else {
3947 for (var _j = 0; _j < A[_v].length; _j++) {
3948 var _w = A[_v][_j].id();
3949
3950 if (d[_w] == Infinity) {
3951 Q.push(_w);
3952 d[_w] = d[_v] + 1;
3953 }
3954
3955 if (d[_w] == d[_v] + 1) {
3956 g[_w] = g[_w] + g[_v];
3957
3958 P[_w].push(_v);
3959 }
3960 }
3961 }
3962 }
3963
3964 var e = {};
3965
3966 for (var _i2 = 0; _i2 < V.length; _i2++) {
3967 e[V[_i2].id()] = 0;
3968 }
3969
3970 while (S.length > 0) {
3971 var _w2 = S.pop();
3972
3973 for (var _j2 = 0; _j2 < P[_w2].length; _j2++) {
3974 var _v2 = P[_w2][_j2];
3975 e[_v2] = e[_v2] + g[_v2] / g[_w2] * (1 + e[_w2]);
3976 }
3977
3978 if (_w2 != V[s].id()) {
3979 C.set(_w2, C.get(_w2) + e[_w2]);
3980 }
3981 }
3982 };
3983
3984 for (var s = 0; s < V.length; s++) {
3985 _loop(s);
3986 }
3987
3988 var ret = {
3989 betweenness: function betweenness(node) {
3990 var id = cy.collection(node).id();
3991 return C.get(id);
3992 },
3993 betweennessNormalized: function betweennessNormalized(node) {
3994 if (max == 0) {
3995 return 0;
3996 }
3997
3998 var id = cy.collection(node).id();
3999 return C.get(id) / max;
4000 }
4001 }; // alias
4002
4003 ret.betweennessNormalised = ret.betweennessNormalized;
4004 return ret;
4005 } // betweennessCentrality
4006
4007}; // elesfn
4008// nice, short mathemathical alias
4009
4010elesfn$a.bc = elesfn$a.betweennessCentrality;
4011
4012// Implemented by Zoe Xi @zoexi for GSOC 2016
4013/* eslint-disable no-unused-vars */
4014
4015var defaults$4 = defaults({
4016 expandFactor: 2,
4017 // affects time of computation and cluster granularity to some extent: M * M
4018 inflateFactor: 2,
4019 // affects cluster granularity (the greater the value, the more clusters): M(i,j) / E(j)
4020 multFactor: 1,
4021 // optional self loops for each node. Use a neutral value to improve cluster computations.
4022 maxIterations: 20,
4023 // maximum number of iterations of the MCL algorithm in a single run
4024 attributes: [// attributes/features used to group nodes, ie. similarity values between nodes
4025 function (edge) {
4026 return 1;
4027 }]
4028});
4029/* eslint-enable */
4030
4031var setOptions = function setOptions(options) {
4032 return defaults$4(options);
4033};
4034/* eslint-enable */
4035
4036
4037var getSimilarity = function getSimilarity(edge, attributes) {
4038 var total = 0;
4039
4040 for (var i = 0; i < attributes.length; i++) {
4041 total += attributes[i](edge);
4042 }
4043
4044 return total;
4045};
4046
4047var addLoops = function addLoops(M, n, val) {
4048 for (var i = 0; i < n; i++) {
4049 M[i * n + i] = val;
4050 }
4051};
4052
4053var normalize = function normalize(M, n) {
4054 var sum;
4055
4056 for (var col = 0; col < n; col++) {
4057 sum = 0;
4058
4059 for (var row = 0; row < n; row++) {
4060 sum += M[row * n + col];
4061 }
4062
4063 for (var _row = 0; _row < n; _row++) {
4064 M[_row * n + col] = M[_row * n + col] / sum;
4065 }
4066 }
4067}; // TODO: blocked matrix multiplication?
4068
4069
4070var mmult = function mmult(A, B, n) {
4071 var C = new Array(n * n);
4072
4073 for (var i = 0; i < n; i++) {
4074 for (var j = 0; j < n; j++) {
4075 C[i * n + j] = 0;
4076 }
4077
4078 for (var k = 0; k < n; k++) {
4079 for (var _j = 0; _j < n; _j++) {
4080 C[i * n + _j] += A[i * n + k] * B[k * n + _j];
4081 }
4082 }
4083 }
4084
4085 return C;
4086};
4087
4088var expand = function expand(M, n, expandFactor
4089/** power **/
4090) {
4091 var _M = M.slice(0);
4092
4093 for (var p = 1; p < expandFactor; p++) {
4094 M = mmult(M, _M, n);
4095 }
4096
4097 return M;
4098};
4099
4100var inflate = function inflate(M, n, inflateFactor
4101/** r **/
4102) {
4103 var _M = new Array(n * n); // M(i,j) ^ inflatePower
4104
4105
4106 for (var i = 0; i < n * n; i++) {
4107 _M[i] = Math.pow(M[i], inflateFactor);
4108 }
4109
4110 normalize(_M, n);
4111 return _M;
4112};
4113
4114var hasConverged = function hasConverged(M, _M, n2, roundFactor) {
4115 // Check that both matrices have the same elements (i,j)
4116 for (var i = 0; i < n2; i++) {
4117 var v1 = Math.round(M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor); // truncate to 'roundFactor' decimal places
4118
4119 var v2 = Math.round(_M[i] * Math.pow(10, roundFactor)) / Math.pow(10, roundFactor);
4120
4121 if (v1 !== v2) {
4122 return false;
4123 }
4124 }
4125
4126 return true;
4127};
4128
4129var assign = function assign(M, n, nodes, cy) {
4130 var clusters = [];
4131
4132 for (var i = 0; i < n; i++) {
4133 var cluster = [];
4134
4135 for (var j = 0; j < n; j++) {
4136 // Row-wise attractors and elements that they attract belong in same cluster
4137 if (Math.round(M[i * n + j] * 1000) / 1000 > 0) {
4138 cluster.push(nodes[j]);
4139 }
4140 }
4141
4142 if (cluster.length !== 0) {
4143 clusters.push(cy.collection(cluster));
4144 }
4145 }
4146
4147 return clusters;
4148};
4149
4150var isDuplicate = function isDuplicate(c1, c2) {
4151 for (var i = 0; i < c1.length; i++) {
4152 if (!c2[i] || c1[i].id() !== c2[i].id()) {
4153 return false;
4154 }
4155 }
4156
4157 return true;
4158};
4159
4160var removeDuplicates = function removeDuplicates(clusters) {
4161 for (var i = 0; i < clusters.length; i++) {
4162 for (var j = 0; j < clusters.length; j++) {
4163 if (i != j && isDuplicate(clusters[i], clusters[j])) {
4164 clusters.splice(j, 1);
4165 }
4166 }
4167 }
4168
4169 return clusters;
4170};
4171
4172var markovClustering = function markovClustering(options) {
4173 var nodes = this.nodes();
4174 var edges = this.edges();
4175 var cy = this.cy(); // Set parameters of algorithm:
4176
4177 var opts = setOptions(options); // Map each node to its position in node array
4178
4179 var id2position = {};
4180
4181 for (var i = 0; i < nodes.length; i++) {
4182 id2position[nodes[i].id()] = i;
4183 } // Generate stochastic matrix M from input graph G (should be symmetric/undirected)
4184
4185
4186 var n = nodes.length,
4187 n2 = n * n;
4188
4189 var M = new Array(n2),
4190 _M;
4191
4192 for (var _i = 0; _i < n2; _i++) {
4193 M[_i] = 0;
4194 }
4195
4196 for (var e = 0; e < edges.length; e++) {
4197 var edge = edges[e];
4198 var _i2 = id2position[edge.source().id()];
4199 var j = id2position[edge.target().id()];
4200 var sim = getSimilarity(edge, opts.attributes);
4201 M[_i2 * n + j] += sim; // G should be symmetric and undirected
4202
4203 M[j * n + _i2] += sim;
4204 } // Begin Markov cluster algorithm
4205 // Step 1: Add self loops to each node, ie. add multFactor to matrix diagonal
4206
4207
4208 addLoops(M, n, opts.multFactor); // Step 2: M = normalize( M );
4209
4210 normalize(M, n);
4211 var isStillMoving = true;
4212 var iterations = 0;
4213
4214 while (isStillMoving && iterations < opts.maxIterations) {
4215 isStillMoving = false; // Step 3:
4216
4217 _M = expand(M, n, opts.expandFactor); // Step 4:
4218
4219 M = inflate(_M, n, opts.inflateFactor); // Step 5: check to see if ~steady state has been reached
4220
4221 if (!hasConverged(M, _M, n2, 4)) {
4222 isStillMoving = true;
4223 }
4224
4225 iterations++;
4226 } // Build clusters from matrix
4227
4228
4229 var clusters = assign(M, n, nodes, cy); // Remove duplicate clusters due to symmetry of graph and M matrix
4230
4231 clusters = removeDuplicates(clusters);
4232 return clusters;
4233};
4234
4235var markovClustering$1 = {
4236 markovClustering: markovClustering,
4237 mcl: markovClustering
4238};
4239
4240// Common distance metrics for clustering algorithms
4241
4242var identity = function identity(x) {
4243 return x;
4244};
4245
4246var absDiff = function absDiff(p, q) {
4247 return Math.abs(q - p);
4248};
4249
4250var addAbsDiff = function addAbsDiff(total, p, q) {
4251 return total + absDiff(p, q);
4252};
4253
4254var addSquaredDiff = function addSquaredDiff(total, p, q) {
4255 return total + Math.pow(q - p, 2);
4256};
4257
4258var sqrt = function sqrt(x) {
4259 return Math.sqrt(x);
4260};
4261
4262var maxAbsDiff = function maxAbsDiff(currentMax, p, q) {
4263 return Math.max(currentMax, absDiff(p, q));
4264};
4265
4266var getDistance = function getDistance(length, getP, getQ, init, visit) {
4267 var post = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : identity;
4268 var ret = init;
4269 var p, q;
4270
4271 for (var dim = 0; dim < length; dim++) {
4272 p = getP(dim);
4273 q = getQ(dim);
4274 ret = visit(ret, p, q);
4275 }
4276
4277 return post(ret);
4278};
4279
4280var distances = {
4281 euclidean: function euclidean(length, getP, getQ) {
4282 if (length >= 2) {
4283 return getDistance(length, getP, getQ, 0, addSquaredDiff, sqrt);
4284 } else {
4285 // for single attr case, more efficient to avoid sqrt
4286 return getDistance(length, getP, getQ, 0, addAbsDiff);
4287 }
4288 },
4289 squaredEuclidean: function squaredEuclidean(length, getP, getQ) {
4290 return getDistance(length, getP, getQ, 0, addSquaredDiff);
4291 },
4292 manhattan: function manhattan(length, getP, getQ) {
4293 return getDistance(length, getP, getQ, 0, addAbsDiff);
4294 },
4295 max: function max(length, getP, getQ) {
4296 return getDistance(length, getP, getQ, -Infinity, maxAbsDiff);
4297 }
4298}; // in case the user accidentally doesn't use camel case
4299
4300distances['squared-euclidean'] = distances['squaredEuclidean'];
4301distances['squaredeuclidean'] = distances['squaredEuclidean'];
4302function clusteringDistance (method, length, getP, getQ, nodeP, nodeQ) {
4303 var impl;
4304
4305 if (fn(method)) {
4306 impl = method;
4307 } else {
4308 impl = distances[method] || distances.euclidean;
4309 }
4310
4311 if (length === 0 && fn(method)) {
4312 return impl(nodeP, nodeQ);
4313 } else {
4314 return impl(length, getP, getQ, nodeP, nodeQ);
4315 }
4316}
4317
4318var defaults$5 = defaults({
4319 k: 2,
4320 m: 2,
4321 sensitivityThreshold: 0.0001,
4322 distance: 'euclidean',
4323 maxIterations: 10,
4324 attributes: [],
4325 testMode: false,
4326 testCentroids: null
4327});
4328
4329var setOptions$1 = function setOptions(options) {
4330 return defaults$5(options);
4331};
4332/* eslint-enable */
4333
4334
4335var getDist = function getDist(type, node, centroid, attributes, mode) {
4336 var noNodeP = mode !== 'kMedoids';
4337 var getP = noNodeP ? function (i) {
4338 return centroid[i];
4339 } : function (i) {
4340 return attributes[i](centroid);
4341 };
4342
4343 var getQ = function getQ(i) {
4344 return attributes[i](node);
4345 };
4346
4347 var nodeP = centroid;
4348 var nodeQ = node;
4349 return clusteringDistance(type, attributes.length, getP, getQ, nodeP, nodeQ);
4350};
4351
4352var randomCentroids = function randomCentroids(nodes, k, attributes) {
4353 var ndim = attributes.length;
4354 var min = new Array(ndim);
4355 var max = new Array(ndim);
4356 var centroids = new Array(k);
4357 var centroid = null; // Find min, max values for each attribute dimension
4358
4359 for (var i = 0; i < ndim; i++) {
4360 min[i] = nodes.min(attributes[i]).value;
4361 max[i] = nodes.max(attributes[i]).value;
4362 } // Build k centroids, each represented as an n-dim feature vector
4363
4364
4365 for (var c = 0; c < k; c++) {
4366 centroid = [];
4367
4368 for (var _i = 0; _i < ndim; _i++) {
4369 centroid[_i] = Math.random() * (max[_i] - min[_i]) + min[_i]; // random initial value
4370 }
4371
4372 centroids[c] = centroid;
4373 }
4374
4375 return centroids;
4376};
4377
4378var classify = function classify(node, centroids, distance, attributes, type) {
4379 var min = Infinity;
4380 var index = 0;
4381
4382 for (var i = 0; i < centroids.length; i++) {
4383 var dist = getDist(distance, node, centroids[i], attributes, type);
4384
4385 if (dist < min) {
4386 min = dist;
4387 index = i;
4388 }
4389 }
4390
4391 return index;
4392};
4393
4394var buildCluster = function buildCluster(centroid, nodes, assignment) {
4395 var cluster = [];
4396 var node = null;
4397
4398 for (var n = 0; n < nodes.length; n++) {
4399 node = nodes[n];
4400
4401 if (assignment[node.id()] === centroid) {
4402 //console.log("Node " + node.id() + " is associated with medoid #: " + m);
4403 cluster.push(node);
4404 }
4405 }
4406
4407 return cluster;
4408};
4409
4410var haveValuesConverged = function haveValuesConverged(v1, v2, sensitivityThreshold) {
4411 return Math.abs(v2 - v1) <= sensitivityThreshold;
4412};
4413
4414var haveMatricesConverged = function haveMatricesConverged(v1, v2, sensitivityThreshold) {
4415 for (var i = 0; i < v1.length; i++) {
4416 for (var j = 0; j < v1[i].length; j++) {
4417 var diff = Math.abs(v1[i][j] - v2[i][j]);
4418
4419 if (diff > sensitivityThreshold) {
4420 return false;
4421 }
4422 }
4423 }
4424
4425 return true;
4426};
4427
4428var seenBefore = function seenBefore(node, medoids, n) {
4429 for (var i = 0; i < n; i++) {
4430 if (node === medoids[i]) return true;
4431 }
4432
4433 return false;
4434};
4435
4436var randomMedoids = function randomMedoids(nodes, k) {
4437 var medoids = new Array(k); // For small data sets, the probability of medoid conflict is greater,
4438 // so we need to check to see if we've already seen or chose this node before.
4439
4440 if (nodes.length < 50) {
4441 // Randomly select k medoids from the n nodes
4442 for (var i = 0; i < k; i++) {
4443 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).
4444 // Instead choose a different random node.
4445
4446 while (seenBefore(node, medoids, i)) {
4447 node = nodes[Math.floor(Math.random() * nodes.length)];
4448 }
4449
4450 medoids[i] = node;
4451 }
4452 } else {
4453 // Relatively large data set, so pretty safe to not check and just select random nodes
4454 for (var _i2 = 0; _i2 < k; _i2++) {
4455 medoids[_i2] = nodes[Math.floor(Math.random() * nodes.length)];
4456 }
4457 }
4458
4459 return medoids;
4460};
4461
4462var findCost = function findCost(potentialNewMedoid, cluster, attributes) {
4463 var cost = 0;
4464
4465 for (var n = 0; n < cluster.length; n++) {
4466 cost += getDist('manhattan', cluster[n], potentialNewMedoid, attributes, 'kMedoids');
4467 }
4468
4469 return cost;
4470};
4471
4472var kMeans = function kMeans(options) {
4473 var cy = this.cy();
4474 var nodes = this.nodes();
4475 var node = null; // Set parameters of algorithm: # of clusters, distance metric, etc.
4476
4477 var opts = setOptions$1(options); // Begin k-means algorithm
4478
4479 var clusters = new Array(opts.k);
4480 var assignment = {};
4481 var centroids; // Step 1: Initialize centroid positions
4482
4483 if (opts.testMode) {
4484 if (typeof opts.testCentroids === 'number') {
4485 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4486 } else if (_typeof(opts.testCentroids) === 'object') {
4487 centroids = opts.testCentroids;
4488 } else {
4489 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4490 }
4491 } else {
4492 centroids = randomCentroids(nodes, opts.k, opts.attributes);
4493 }
4494
4495 var isStillMoving = true;
4496 var iterations = 0;
4497
4498 while (isStillMoving && iterations < opts.maxIterations) {
4499 // Step 2: Assign nodes to the nearest centroid
4500 for (var n = 0; n < nodes.length; n++) {
4501 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4502
4503 assignment[node.id()] = classify(node, centroids, opts.distance, opts.attributes, 'kMeans');
4504 } // Step 3: For each of the k clusters, update its centroid
4505
4506
4507 isStillMoving = false;
4508
4509 for (var c = 0; c < opts.k; c++) {
4510 // Get all nodes that belong to this cluster
4511 var cluster = buildCluster(c, nodes, assignment);
4512
4513 if (cluster.length === 0) {
4514 // If cluster is empty, break out early & move to next cluster
4515 continue;
4516 } // Update centroids by calculating avg of all nodes within the cluster.
4517
4518
4519 var ndim = opts.attributes.length;
4520 var centroid = centroids[c]; // [ dim_1, dim_2, dim_3, ... , dim_n ]
4521
4522 var newCentroid = new Array(ndim);
4523 var sum = new Array(ndim);
4524
4525 for (var d = 0; d < ndim; d++) {
4526 sum[d] = 0.0;
4527
4528 for (var i = 0; i < cluster.length; i++) {
4529 node = cluster[i];
4530 sum[d] += opts.attributes[d](node);
4531 }
4532
4533 newCentroid[d] = sum[d] / cluster.length; // Check to see if algorithm has converged, i.e. when centroids no longer change
4534
4535 if (!haveValuesConverged(newCentroid[d], centroid[d], opts.sensitivityThreshold)) {
4536 isStillMoving = true;
4537 }
4538 }
4539
4540 centroids[c] = newCentroid;
4541 clusters[c] = cy.collection(cluster);
4542 }
4543
4544 iterations++;
4545 }
4546
4547 return clusters;
4548};
4549
4550var kMedoids = function kMedoids(options) {
4551 var cy = this.cy();
4552 var nodes = this.nodes();
4553 var node = null;
4554 var opts = setOptions$1(options); // Begin k-medoids algorithm
4555
4556 var clusters = new Array(opts.k);
4557 var medoids;
4558 var assignment = {};
4559 var curCost;
4560 var minCosts = new Array(opts.k); // minimum cost configuration for each cluster
4561 // Step 1: Initialize k medoids
4562
4563 if (opts.testMode) {
4564 if (typeof opts.testCentroids === 'number') ; else if (_typeof(opts.testCentroids) === 'object') {
4565 medoids = opts.testCentroids;
4566 } else {
4567 medoids = randomMedoids(nodes, opts.k);
4568 }
4569 } else {
4570 medoids = randomMedoids(nodes, opts.k);
4571 }
4572
4573 var isStillMoving = true;
4574 var iterations = 0;
4575
4576 while (isStillMoving && iterations < opts.maxIterations) {
4577 // Step 2: Assign nodes to the nearest medoid
4578 for (var n = 0; n < nodes.length; n++) {
4579 node = nodes[n]; // Determine which cluster this node belongs to: node id => cluster #
4580
4581 assignment[node.id()] = classify(node, medoids, opts.distance, opts.attributes, 'kMedoids');
4582 }
4583
4584 isStillMoving = false; // Step 3: For each medoid m, and for each node assciated with mediod m,
4585 // select the node with the lowest configuration cost as new medoid.
4586
4587 for (var m = 0; m < medoids.length; m++) {
4588 // Get all nodes that belong to this medoid
4589 var cluster = buildCluster(m, nodes, assignment);
4590
4591 if (cluster.length === 0) {
4592 // If cluster is empty, break out early & move to next cluster
4593 continue;
4594 }
4595
4596 minCosts[m] = findCost(medoids[m], cluster, opts.attributes); // original cost
4597 // Select different medoid if its configuration has the lowest cost
4598
4599 for (var _n = 0; _n < cluster.length; _n++) {
4600 curCost = findCost(cluster[_n], cluster, opts.attributes);
4601
4602 if (curCost < minCosts[m]) {
4603 minCosts[m] = curCost;
4604 medoids[m] = cluster[_n];
4605 isStillMoving = true;
4606 }
4607 }
4608
4609 clusters[m] = cy.collection(cluster);
4610 }
4611
4612 iterations++;
4613 }
4614
4615 return clusters;
4616};
4617
4618var updateCentroids = function updateCentroids(centroids, nodes, U, weight, opts) {
4619 var numerator, denominator;
4620
4621 for (var n = 0; n < nodes.length; n++) {
4622 for (var c = 0; c < centroids.length; c++) {
4623 weight[n][c] = Math.pow(U[n][c], opts.m);
4624 }
4625 }
4626
4627 for (var _c = 0; _c < centroids.length; _c++) {
4628 for (var dim = 0; dim < opts.attributes.length; dim++) {
4629 numerator = 0;
4630 denominator = 0;
4631
4632 for (var _n2 = 0; _n2 < nodes.length; _n2++) {
4633 numerator += weight[_n2][_c] * opts.attributes[dim](nodes[_n2]);
4634 denominator += weight[_n2][_c];
4635 }
4636
4637 centroids[_c][dim] = numerator / denominator;
4638 }
4639 }
4640};
4641
4642var updateMembership = function updateMembership(U, _U, centroids, nodes, opts) {
4643 // Save previous step
4644 for (var i = 0; i < U.length; i++) {
4645 _U[i] = U[i].slice();
4646 }
4647
4648 var sum, numerator, denominator;
4649 var pow = 2 / (opts.m - 1);
4650
4651 for (var c = 0; c < centroids.length; c++) {
4652 for (var n = 0; n < nodes.length; n++) {
4653 sum = 0;
4654
4655 for (var k = 0; k < centroids.length; k++) {
4656 // against all other centroids
4657 numerator = getDist(opts.distance, nodes[n], centroids[c], opts.attributes, 'cmeans');
4658 denominator = getDist(opts.distance, nodes[n], centroids[k], opts.attributes, 'cmeans');
4659 sum += Math.pow(numerator / denominator, pow);
4660 }
4661
4662 U[n][c] = 1 / sum;
4663 }
4664 }
4665};
4666
4667var assign$1 = function assign(nodes, U, opts, cy) {
4668 var clusters = new Array(opts.k);
4669
4670 for (var c = 0; c < clusters.length; c++) {
4671 clusters[c] = [];
4672 }
4673
4674 var max;
4675 var index;
4676
4677 for (var n = 0; n < U.length; n++) {
4678 // for each node (U is N x C matrix)
4679 max = -Infinity;
4680 index = -1; // Determine which cluster the node is most likely to belong in
4681
4682 for (var _c2 = 0; _c2 < U[0].length; _c2++) {
4683 if (U[n][_c2] > max) {
4684 max = U[n][_c2];
4685 index = _c2;
4686 }
4687 }
4688
4689 clusters[index].push(nodes[n]);
4690 } // Turn every array into a collection of nodes
4691
4692
4693 for (var _c3 = 0; _c3 < clusters.length; _c3++) {
4694 clusters[_c3] = cy.collection(clusters[_c3]);
4695 }
4696
4697 return clusters;
4698};
4699
4700var fuzzyCMeans = function fuzzyCMeans(options) {
4701 var cy = this.cy();
4702 var nodes = this.nodes();
4703 var opts = setOptions$1(options); // Begin fuzzy c-means algorithm
4704
4705 var clusters;
4706 var centroids;
4707 var U;
4708
4709 var _U;
4710
4711 var weight; // Step 1: Initialize letiables.
4712
4713 _U = new Array(nodes.length);
4714
4715 for (var i = 0; i < nodes.length; i++) {
4716 // N x C matrix
4717 _U[i] = new Array(opts.k);
4718 }
4719
4720 U = new Array(nodes.length);
4721
4722 for (var _i3 = 0; _i3 < nodes.length; _i3++) {
4723 // N x C matrix
4724 U[_i3] = new Array(opts.k);
4725 }
4726
4727 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
4728 var total = 0;
4729
4730 for (var j = 0; j < opts.k; j++) {
4731 U[_i4][j] = Math.random();
4732 total += U[_i4][j];
4733 }
4734
4735 for (var _j = 0; _j < opts.k; _j++) {
4736 U[_i4][_j] = U[_i4][_j] / total;
4737 }
4738 }
4739
4740 centroids = new Array(opts.k);
4741
4742 for (var _i5 = 0; _i5 < opts.k; _i5++) {
4743 centroids[_i5] = new Array(opts.attributes.length);
4744 }
4745
4746 weight = new Array(nodes.length);
4747
4748 for (var _i6 = 0; _i6 < nodes.length; _i6++) {
4749 // N x C matrix
4750 weight[_i6] = new Array(opts.k);
4751 } // end init FCM
4752
4753
4754 var isStillMoving = true;
4755 var iterations = 0;
4756
4757 while (isStillMoving && iterations < opts.maxIterations) {
4758 isStillMoving = false; // Step 2: Calculate the centroids for each step.
4759
4760 updateCentroids(centroids, nodes, U, weight, opts); // Step 3: Update the partition matrix U.
4761
4762 updateMembership(U, _U, centroids, nodes, opts); // Step 4: Check for convergence.
4763
4764 if (!haveMatricesConverged(U, _U, opts.sensitivityThreshold)) {
4765 isStillMoving = true;
4766 }
4767
4768 iterations++;
4769 } // Assign nodes to clusters with highest probability.
4770
4771
4772 clusters = assign$1(nodes, U, opts, cy);
4773 return {
4774 clusters: clusters,
4775 degreeOfMembership: U
4776 };
4777};
4778
4779var kClustering = {
4780 kMeans: kMeans,
4781 kMedoids: kMedoids,
4782 fuzzyCMeans: fuzzyCMeans,
4783 fcm: fuzzyCMeans
4784};
4785
4786// Implemented by Zoe Xi @zoexi for GSOC 2016
4787var defaults$6 = defaults({
4788 distance: 'euclidean',
4789 // distance metric to compare nodes
4790 linkage: 'min',
4791 // linkage criterion : how to determine the distance between clusters of nodes
4792 mode: 'threshold',
4793 // mode:'threshold' => clusters must be threshold distance apart
4794 threshold: Infinity,
4795 // the distance threshold
4796 // mode:'dendrogram' => the nodes are organised as leaves in a tree (siblings are close), merging makes clusters
4797 addDendrogram: false,
4798 // whether to add the dendrogram to the graph for viz
4799 dendrogramDepth: 0,
4800 // depth at which dendrogram branches are merged into the returned clusters
4801 attributes: [] // array of attr functions
4802
4803});
4804var linkageAliases = {
4805 'single': 'min',
4806 'complete': 'max'
4807};
4808
4809var setOptions$2 = function setOptions(options) {
4810 var opts = defaults$6(options);
4811 var preferredAlias = linkageAliases[opts.linkage];
4812
4813 if (preferredAlias != null) {
4814 opts.linkage = preferredAlias;
4815 }
4816
4817 return opts;
4818};
4819
4820var mergeClosest = function mergeClosest(clusters, index, dists, mins, opts) {
4821 // Find two closest clusters from cached mins
4822 var minKey = 0;
4823 var min = Infinity;
4824 var dist;
4825 var attrs = opts.attributes;
4826
4827 var getDist = function getDist(n1, n2) {
4828 return clusteringDistance(opts.distance, attrs.length, function (i) {
4829 return attrs[i](n1);
4830 }, function (i) {
4831 return attrs[i](n2);
4832 }, n1, n2);
4833 };
4834
4835 for (var i = 0; i < clusters.length; i++) {
4836 var key = clusters[i].key;
4837 var _dist = dists[key][mins[key]];
4838
4839 if (_dist < min) {
4840 minKey = key;
4841 min = _dist;
4842 }
4843 }
4844
4845 if (opts.mode === 'threshold' && min >= opts.threshold || opts.mode === 'dendrogram' && clusters.length === 1) {
4846 return false;
4847 }
4848
4849 var c1 = index[minKey];
4850 var c2 = index[mins[minKey]];
4851 var merged; // Merge two closest clusters
4852
4853 if (opts.mode === 'dendrogram') {
4854 merged = {
4855 left: c1,
4856 right: c2,
4857 key: c1.key
4858 };
4859 } else {
4860 merged = {
4861 value: c1.value.concat(c2.value),
4862 key: c1.key
4863 };
4864 }
4865
4866 clusters[c1.index] = merged;
4867 clusters.splice(c2.index, 1);
4868 index[c1.key] = merged; // Update distances with new merged cluster
4869
4870 for (var _i = 0; _i < clusters.length; _i++) {
4871 var cur = clusters[_i];
4872
4873 if (c1.key === cur.key) {
4874 dist = Infinity;
4875 } else if (opts.linkage === 'min') {
4876 dist = dists[c1.key][cur.key];
4877
4878 if (dists[c1.key][cur.key] > dists[c2.key][cur.key]) {
4879 dist = dists[c2.key][cur.key];
4880 }
4881 } else if (opts.linkage === 'max') {
4882 dist = dists[c1.key][cur.key];
4883
4884 if (dists[c1.key][cur.key] < dists[c2.key][cur.key]) {
4885 dist = dists[c2.key][cur.key];
4886 }
4887 } else if (opts.linkage === 'mean') {
4888 dist = (dists[c1.key][cur.key] * c1.size + dists[c2.key][cur.key] * c2.size) / (c1.size + c2.size);
4889 } else {
4890 if (opts.mode === 'dendrogram') dist = getDist(cur.value, c1.value);else dist = getDist(cur.value[0], c1.value[0]);
4891 }
4892
4893 dists[c1.key][cur.key] = dists[cur.key][c1.key] = dist; // distance matrix is symmetric
4894 } // Update cached mins
4895
4896
4897 for (var _i2 = 0; _i2 < clusters.length; _i2++) {
4898 var key1 = clusters[_i2].key;
4899
4900 if (mins[key1] === c1.key || mins[key1] === c2.key) {
4901 var _min = key1;
4902
4903 for (var j = 0; j < clusters.length; j++) {
4904 var key2 = clusters[j].key;
4905
4906 if (dists[key1][key2] < dists[key1][_min]) {
4907 _min = key2;
4908 }
4909 }
4910
4911 mins[key1] = _min;
4912 }
4913
4914 clusters[_i2].index = _i2;
4915 } // Clean up meta data used for clustering
4916
4917
4918 c1.key = c2.key = c1.index = c2.index = null;
4919 return true;
4920};
4921
4922var getAllChildren = function getAllChildren(root, arr, cy) {
4923 if (!root) return;
4924
4925 if (root.value) {
4926 arr.push(root.value);
4927 } else {
4928 if (root.left) getAllChildren(root.left, arr);
4929 if (root.right) getAllChildren(root.right, arr);
4930 }
4931};
4932
4933var buildDendrogram = function buildDendrogram(root, cy) {
4934 if (!root) return '';
4935
4936 if (root.left && root.right) {
4937 var leftStr = buildDendrogram(root.left, cy);
4938 var rightStr = buildDendrogram(root.right, cy);
4939 var node = cy.add({
4940 group: 'nodes',
4941 data: {
4942 id: leftStr + ',' + rightStr
4943 }
4944 });
4945 cy.add({
4946 group: 'edges',
4947 data: {
4948 source: leftStr,
4949 target: node.id()
4950 }
4951 });
4952 cy.add({
4953 group: 'edges',
4954 data: {
4955 source: rightStr,
4956 target: node.id()
4957 }
4958 });
4959 return node.id();
4960 } else if (root.value) {
4961 return root.value.id();
4962 }
4963};
4964
4965var buildClustersFromTree = function buildClustersFromTree(root, k, cy) {
4966 if (!root) return [];
4967 var left = [],
4968 right = [],
4969 leaves = [];
4970
4971 if (k === 0) {
4972 // don't cut tree, simply return all nodes as 1 single cluster
4973 if (root.left) getAllChildren(root.left, left);
4974 if (root.right) getAllChildren(root.right, right);
4975 leaves = left.concat(right);
4976 return [cy.collection(leaves)];
4977 } else if (k === 1) {
4978 // cut at root
4979 if (root.value) {
4980 // leaf node
4981 return [cy.collection(root.value)];
4982 } else {
4983 if (root.left) getAllChildren(root.left, left);
4984 if (root.right) getAllChildren(root.right, right);
4985 return [cy.collection(left), cy.collection(right)];
4986 }
4987 } else {
4988 if (root.value) {
4989 return [cy.collection(root.value)];
4990 } else {
4991 if (root.left) left = buildClustersFromTree(root.left, k - 1, cy);
4992 if (root.right) right = buildClustersFromTree(root.right, k - 1, cy);
4993 return left.concat(right);
4994 }
4995 }
4996};
4997/* eslint-enable */
4998
4999
5000var hierarchicalClustering = function hierarchicalClustering(options) {
5001 var cy = this.cy();
5002 var nodes = this.nodes(); // Set parameters of algorithm: linkage type, distance metric, etc.
5003
5004 var opts = setOptions$2(options);
5005 var attrs = opts.attributes;
5006
5007 var getDist = function getDist(n1, n2) {
5008 return clusteringDistance(opts.distance, attrs.length, function (i) {
5009 return attrs[i](n1);
5010 }, function (i) {
5011 return attrs[i](n2);
5012 }, n1, n2);
5013 }; // Begin hierarchical algorithm
5014
5015
5016 var clusters = [];
5017 var dists = []; // distances between each pair of clusters
5018
5019 var mins = []; // closest cluster for each cluster
5020
5021 var index = []; // hash of all clusters by key
5022 // In agglomerative (bottom-up) clustering, each node starts as its own cluster
5023
5024 for (var n = 0; n < nodes.length; n++) {
5025 var cluster = {
5026 value: opts.mode === 'dendrogram' ? nodes[n] : [nodes[n]],
5027 key: n,
5028 index: n
5029 };
5030 clusters[n] = cluster;
5031 index[n] = cluster;
5032 dists[n] = [];
5033 mins[n] = 0;
5034 } // Calculate the distance between each pair of clusters
5035
5036
5037 for (var i = 0; i < clusters.length; i++) {
5038 for (var j = 0; j <= i; j++) {
5039 var dist = void 0;
5040
5041 if (opts.mode === 'dendrogram') {
5042 // modes store cluster values differently
5043 dist = i === j ? Infinity : getDist(clusters[i].value, clusters[j].value);
5044 } else {
5045 dist = i === j ? Infinity : getDist(clusters[i].value[0], clusters[j].value[0]);
5046 }
5047
5048 dists[i][j] = dist;
5049 dists[j][i] = dist;
5050
5051 if (dist < dists[i][mins[i]]) {
5052 mins[i] = j; // Cache mins: closest cluster to cluster i is cluster j
5053 }
5054 }
5055 } // Find the closest pair of clusters and merge them into a single cluster.
5056 // Update distances between new cluster and each of the old clusters, and loop until threshold reached.
5057
5058
5059 var merged = mergeClosest(clusters, index, dists, mins, opts);
5060
5061 while (merged) {
5062 merged = mergeClosest(clusters, index, dists, mins, opts);
5063 }
5064
5065 var retClusters; // Dendrogram mode builds the hierarchy and adds intermediary nodes + edges
5066 // in addition to returning the clusters.
5067
5068 if (opts.mode === 'dendrogram') {
5069 retClusters = buildClustersFromTree(clusters[0], opts.dendrogramDepth, cy);
5070 if (opts.addDendrogram) buildDendrogram(clusters[0], cy);
5071 } else {
5072 // Regular mode simply returns the clusters
5073 retClusters = new Array(clusters.length);
5074 clusters.forEach(function (cluster, i) {
5075 // Clean up meta data used for clustering
5076 cluster.key = cluster.index = null;
5077 retClusters[i] = cy.collection(cluster.value);
5078 });
5079 }
5080
5081 return retClusters;
5082};
5083
5084var hierarchicalClustering$1 = {
5085 hierarchicalClustering: hierarchicalClustering,
5086 hca: hierarchicalClustering
5087};
5088
5089// Implemented by Zoe Xi @zoexi for GSOC 2016
5090var defaults$7 = defaults({
5091 distance: 'euclidean',
5092 // distance metric to compare attributes between two nodes
5093 preference: 'median',
5094 // suitability of a data point to serve as an exemplar
5095 damping: 0.8,
5096 // damping factor between [0.5, 1)
5097 maxIterations: 1000,
5098 // max number of iterations to run
5099 minIterations: 100,
5100 // min number of iterations to run in order for clustering to stop
5101 attributes: [// functions to quantify the similarity between any two points
5102 // e.g. node => node.data('weight')
5103 ]
5104});
5105
5106var setOptions$3 = function setOptions(options) {
5107 var dmp = options.damping;
5108 var pref = options.preference;
5109
5110 if (!(0.5 <= dmp && dmp < 1)) {
5111 error("Damping must range on [0.5, 1). Got: ".concat(dmp));
5112 }
5113
5114 var validPrefs = ['median', 'mean', 'min', 'max'];
5115
5116 if (!(validPrefs.some(function (v) {
5117 return v === pref;
5118 }) || number(pref))) {
5119 error("Preference must be one of [".concat(validPrefs.map(function (p) {
5120 return "'".concat(p, "'");
5121 }).join(', '), "] or a number. Got: ").concat(pref));
5122 }
5123
5124 return defaults$7(options);
5125};
5126/* eslint-enable */
5127
5128
5129var getSimilarity$1 = function getSimilarity(type, n1, n2, attributes) {
5130 var attr = function attr(n, i) {
5131 return attributes[i](n);
5132 }; // nb negative because similarity should have an inverse relationship to distance
5133
5134
5135 return -clusteringDistance(type, attributes.length, function (i) {
5136 return attr(n1, i);
5137 }, function (i) {
5138 return attr(n2, i);
5139 }, n1, n2);
5140};
5141
5142var getPreference = function getPreference(S, preference) {
5143 // larger preference = greater # of clusters
5144 var p = null;
5145
5146 if (preference === 'median') {
5147 p = median(S);
5148 } else if (preference === 'mean') {
5149 p = mean(S);
5150 } else if (preference === 'min') {
5151 p = min(S);
5152 } else if (preference === 'max') {
5153 p = max(S);
5154 } else {
5155 // Custom preference number, as set by user
5156 p = preference;
5157 }
5158
5159 return p;
5160};
5161
5162var findExemplars = function findExemplars(n, R, A) {
5163 var indices = [];
5164
5165 for (var i = 0; i < n; i++) {
5166 if (R[i * n + i] + A[i * n + i] > 0) {
5167 indices.push(i);
5168 }
5169 }
5170
5171 return indices;
5172};
5173
5174var assignClusters = function assignClusters(n, S, exemplars) {
5175 var clusters = [];
5176
5177 for (var i = 0; i < n; i++) {
5178 var index = -1;
5179 var max = -Infinity;
5180
5181 for (var ei = 0; ei < exemplars.length; ei++) {
5182 var e = exemplars[ei];
5183
5184 if (S[i * n + e] > max) {
5185 index = e;
5186 max = S[i * n + e];
5187 }
5188 }
5189
5190 if (index > 0) {
5191 clusters.push(index);
5192 }
5193 }
5194
5195 for (var _ei = 0; _ei < exemplars.length; _ei++) {
5196 clusters[exemplars[_ei]] = exemplars[_ei];
5197 }
5198
5199 return clusters;
5200};
5201
5202var assign$2 = function assign(n, S, exemplars) {
5203 var clusters = assignClusters(n, S, exemplars);
5204
5205 for (var ei = 0; ei < exemplars.length; ei++) {
5206 var ii = [];
5207
5208 for (var c = 0; c < clusters.length; c++) {
5209 if (clusters[c] === exemplars[ei]) {
5210 ii.push(c);
5211 }
5212 }
5213
5214 var maxI = -1;
5215 var maxSum = -Infinity;
5216
5217 for (var i = 0; i < ii.length; i++) {
5218 var sum = 0;
5219
5220 for (var j = 0; j < ii.length; j++) {
5221 sum += S[ii[j] * n + ii[i]];
5222 }
5223
5224 if (sum > maxSum) {
5225 maxI = i;
5226 maxSum = sum;
5227 }
5228 }
5229
5230 exemplars[ei] = ii[maxI];
5231 }
5232
5233 clusters = assignClusters(n, S, exemplars);
5234 return clusters;
5235};
5236
5237var affinityPropagation = function affinityPropagation(options) {
5238 var cy = this.cy();
5239 var nodes = this.nodes();
5240 var opts = setOptions$3(options); // Map each node to its position in node array
5241
5242 var id2position = {};
5243
5244 for (var i = 0; i < nodes.length; i++) {
5245 id2position[nodes[i].id()] = i;
5246 } // Begin affinity propagation algorithm
5247
5248
5249 var n; // number of data points
5250
5251 var n2; // size of matrices
5252
5253 var S; // similarity matrix (1D array)
5254
5255 var p; // preference/suitability of a data point to serve as an exemplar
5256
5257 var R; // responsibility matrix (1D array)
5258
5259 var A; // availability matrix (1D array)
5260
5261 n = nodes.length;
5262 n2 = n * n; // Initialize and build S similarity matrix
5263
5264 S = new Array(n2);
5265
5266 for (var _i = 0; _i < n2; _i++) {
5267 S[_i] = -Infinity; // for cases where two data points shouldn't be linked together
5268 }
5269
5270 for (var _i2 = 0; _i2 < n; _i2++) {
5271 for (var j = 0; j < n; j++) {
5272 if (_i2 !== j) {
5273 S[_i2 * n + j] = getSimilarity$1(opts.distance, nodes[_i2], nodes[j], opts.attributes);
5274 }
5275 }
5276 } // Place preferences on the diagonal of S
5277
5278
5279 p = getPreference(S, opts.preference);
5280
5281 for (var _i3 = 0; _i3 < n; _i3++) {
5282 S[_i3 * n + _i3] = p;
5283 } // Initialize R responsibility matrix
5284
5285
5286 R = new Array(n2);
5287
5288 for (var _i4 = 0; _i4 < n2; _i4++) {
5289 R[_i4] = 0.0;
5290 } // Initialize A availability matrix
5291
5292
5293 A = new Array(n2);
5294
5295 for (var _i5 = 0; _i5 < n2; _i5++) {
5296 A[_i5] = 0.0;
5297 }
5298
5299 var old = new Array(n);
5300 var Rp = new Array(n);
5301 var se = new Array(n);
5302
5303 for (var _i6 = 0; _i6 < n; _i6++) {
5304 old[_i6] = 0.0;
5305 Rp[_i6] = 0.0;
5306 se[_i6] = 0;
5307 }
5308
5309 var e = new Array(n * opts.minIterations);
5310
5311 for (var _i7 = 0; _i7 < e.length; _i7++) {
5312 e[_i7] = 0;
5313 }
5314
5315 var iter;
5316
5317 for (iter = 0; iter < opts.maxIterations; iter++) {
5318 // main algorithmic loop
5319 // Update R responsibility matrix
5320 for (var _i8 = 0; _i8 < n; _i8++) {
5321 var max = -Infinity,
5322 max2 = -Infinity,
5323 maxI = -1,
5324 AS = 0.0;
5325
5326 for (var _j = 0; _j < n; _j++) {
5327 old[_j] = R[_i8 * n + _j];
5328 AS = A[_i8 * n + _j] + S[_i8 * n + _j];
5329
5330 if (AS >= max) {
5331 max2 = max;
5332 max = AS;
5333 maxI = _j;
5334 } else if (AS > max2) {
5335 max2 = AS;
5336 }
5337 }
5338
5339 for (var _j2 = 0; _j2 < n; _j2++) {
5340 R[_i8 * n + _j2] = (1 - opts.damping) * (S[_i8 * n + _j2] - max) + opts.damping * old[_j2];
5341 }
5342
5343 R[_i8 * n + maxI] = (1 - opts.damping) * (S[_i8 * n + maxI] - max2) + opts.damping * old[maxI];
5344 } // Update A availability matrix
5345
5346
5347 for (var _i9 = 0; _i9 < n; _i9++) {
5348 var sum = 0;
5349
5350 for (var _j3 = 0; _j3 < n; _j3++) {
5351 old[_j3] = A[_j3 * n + _i9];
5352 Rp[_j3] = Math.max(0, R[_j3 * n + _i9]);
5353 sum += Rp[_j3];
5354 }
5355
5356 sum -= Rp[_i9];
5357 Rp[_i9] = R[_i9 * n + _i9];
5358 sum += Rp[_i9];
5359
5360 for (var _j4 = 0; _j4 < n; _j4++) {
5361 A[_j4 * n + _i9] = (1 - opts.damping) * Math.min(0, sum - Rp[_j4]) + opts.damping * old[_j4];
5362 }
5363
5364 A[_i9 * n + _i9] = (1 - opts.damping) * (sum - Rp[_i9]) + opts.damping * old[_i9];
5365 } // Check for convergence
5366
5367
5368 var K = 0;
5369
5370 for (var _i10 = 0; _i10 < n; _i10++) {
5371 var E = A[_i10 * n + _i10] + R[_i10 * n + _i10] > 0 ? 1 : 0;
5372 e[iter % opts.minIterations * n + _i10] = E;
5373 K += E;
5374 }
5375
5376 if (K > 0 && (iter >= opts.minIterations - 1 || iter == opts.maxIterations - 1)) {
5377 var _sum = 0;
5378
5379 for (var _i11 = 0; _i11 < n; _i11++) {
5380 se[_i11] = 0;
5381
5382 for (var _j5 = 0; _j5 < opts.minIterations; _j5++) {
5383 se[_i11] += e[_j5 * n + _i11];
5384 }
5385
5386 if (se[_i11] === 0 || se[_i11] === opts.minIterations) {
5387 _sum++;
5388 }
5389 }
5390
5391 if (_sum === n) {
5392 // then we have convergence
5393 break;
5394 }
5395 }
5396 } // Identify exemplars (cluster centers)
5397
5398
5399 var exemplarsIndices = findExemplars(n, R, A); // Assign nodes to clusters
5400
5401 var clusterIndices = assign$2(n, S, exemplarsIndices);
5402 var clusters = {};
5403
5404 for (var c = 0; c < exemplarsIndices.length; c++) {
5405 clusters[exemplarsIndices[c]] = [];
5406 }
5407
5408 for (var _i12 = 0; _i12 < nodes.length; _i12++) {
5409 var pos = id2position[nodes[_i12].id()];
5410
5411 var clusterIndex = clusterIndices[pos];
5412
5413 if (clusterIndex != null) {
5414 // the node may have not been assigned a cluster if no valid attributes were specified
5415 clusters[clusterIndex].push(nodes[_i12]);
5416 }
5417 }
5418
5419 var retClusters = new Array(exemplarsIndices.length);
5420
5421 for (var _c = 0; _c < exemplarsIndices.length; _c++) {
5422 retClusters[_c] = cy.collection(clusters[exemplarsIndices[_c]]);
5423 }
5424
5425 return retClusters;
5426};
5427
5428var affinityPropagation$1 = {
5429 affinityPropagation: affinityPropagation,
5430 ap: affinityPropagation
5431};
5432
5433var hierholzerDefaults = defaults({
5434 root: undefined,
5435 directed: false
5436});
5437var elesfn$b = {
5438 hierholzer: function hierholzer(options) {
5439 if (!plainObject(options)) {
5440 var args = arguments;
5441 options = {
5442 root: args[0],
5443 directed: args[1]
5444 };
5445 }
5446
5447 var _hierholzerDefaults = hierholzerDefaults(options),
5448 root = _hierholzerDefaults.root,
5449 directed = _hierholzerDefaults.directed;
5450
5451 var eles = this;
5452 var dflag = false;
5453 var oddIn;
5454 var oddOut;
5455 var startVertex;
5456 if (root) startVertex = string(root) ? this.filter(root)[0].id() : root[0].id();
5457 var nodes = {};
5458 var edges = {};
5459
5460 if (directed) {
5461 eles.forEach(function (ele) {
5462 var id = ele.id();
5463
5464 if (ele.isNode()) {
5465 var ind = ele.indegree(true);
5466 var outd = ele.outdegree(true);
5467 var d1 = ind - outd;
5468 var d2 = outd - ind;
5469
5470 if (d1 == 1) {
5471 if (oddIn) dflag = true;else oddIn = id;
5472 } else if (d2 == 1) {
5473 if (oddOut) dflag = true;else oddOut = id;
5474 } else if (d2 > 1 || d1 > 1) {
5475 dflag = true;
5476 }
5477
5478 nodes[id] = [];
5479 ele.outgoers().forEach(function (e) {
5480 if (e.isEdge()) nodes[id].push(e.id());
5481 });
5482 } else {
5483 edges[id] = [undefined, ele.target().id()];
5484 }
5485 });
5486 } else {
5487 eles.forEach(function (ele) {
5488 var id = ele.id();
5489
5490 if (ele.isNode()) {
5491 var d = ele.degree(true);
5492
5493 if (d % 2) {
5494 if (!oddIn) oddIn = id;else if (!oddOut) oddOut = id;else dflag = true;
5495 }
5496
5497 nodes[id] = [];
5498 ele.connectedEdges().forEach(function (e) {
5499 return nodes[id].push(e.id());
5500 });
5501 } else {
5502 edges[id] = [ele.source().id(), ele.target().id()];
5503 }
5504 });
5505 }
5506
5507 var result = {
5508 found: false,
5509 trail: undefined
5510 };
5511 if (dflag) return result;else if (oddOut && oddIn) {
5512 if (directed) {
5513 if (startVertex && oddOut != startVertex) {
5514 return result;
5515 }
5516
5517 startVertex = oddOut;
5518 } else {
5519 if (startVertex && oddOut != startVertex && oddIn != startVertex) {
5520 return result;
5521 } else if (!startVertex) {
5522 startVertex = oddOut;
5523 }
5524 }
5525 } else {
5526 if (!startVertex) startVertex = eles[0].id();
5527 }
5528
5529 var walk = function walk(v) {
5530 var currentNode = v;
5531 var subtour = [v];
5532 var adj, adjTail, adjHead;
5533
5534 while (nodes[currentNode].length) {
5535 adj = nodes[currentNode].shift();
5536 adjTail = edges[adj][0];
5537 adjHead = edges[adj][1];
5538
5539 if (currentNode != adjHead) {
5540 nodes[adjHead] = nodes[adjHead].filter(function (e) {
5541 return e != adj;
5542 });
5543 currentNode = adjHead;
5544 } else if (!directed && currentNode != adjTail) {
5545 nodes[adjTail] = nodes[adjTail].filter(function (e) {
5546 return e != adj;
5547 });
5548 currentNode = adjTail;
5549 }
5550
5551 subtour.unshift(adj);
5552 subtour.unshift(currentNode);
5553 }
5554
5555 return subtour;
5556 };
5557
5558 var trail = [];
5559 var subtour = [];
5560 subtour = walk(startVertex);
5561
5562 while (subtour.length != 1) {
5563 if (nodes[subtour[0]].length == 0) {
5564 trail.unshift(eles.getElementById(subtour.shift()));
5565 trail.unshift(eles.getElementById(subtour.shift()));
5566 } else {
5567 subtour = walk(subtour.shift()).concat(subtour);
5568 }
5569 }
5570
5571 trail.unshift(eles.getElementById(subtour.shift())); // final node
5572
5573 for (var d in nodes) {
5574 if (nodes[d].length) {
5575 return result;
5576 }
5577 }
5578
5579 result.found = true;
5580 result.trail = this.spawn(trail, true);
5581 return result;
5582 }
5583};
5584
5585var hopcroftTarjanBiconnected = function hopcroftTarjanBiconnected() {
5586 var eles = this;
5587 var nodes = {};
5588 var id = 0;
5589 var edgeCount = 0;
5590 var components = [];
5591 var stack = [];
5592 var visitedEdges = {};
5593
5594 var buildComponent = function buildComponent(x, y) {
5595 var i = stack.length - 1;
5596 var cutset = [];
5597 var component = eles.spawn();
5598
5599 while (stack[i].x != x || stack[i].y != y) {
5600 cutset.push(stack.pop().edge);
5601 i--;
5602 }
5603
5604 cutset.push(stack.pop().edge);
5605 cutset.forEach(function (edge) {
5606 var connectedNodes = edge.connectedNodes().intersection(eles);
5607 component.merge(edge);
5608 connectedNodes.forEach(function (node) {
5609 var nodeId = node.id();
5610 var connectedEdges = node.connectedEdges().intersection(eles);
5611 component.merge(node);
5612
5613 if (!nodes[nodeId].cutVertex) {
5614 component.merge(connectedEdges);
5615 } else {
5616 component.merge(connectedEdges.filter(function (edge) {
5617 return edge.isLoop();
5618 }));
5619 }
5620 });
5621 });
5622 components.push(component);
5623 };
5624
5625 var biconnectedSearch = function biconnectedSearch(root, currentNode, parent) {
5626 if (root === parent) edgeCount += 1;
5627 nodes[currentNode] = {
5628 id: id,
5629 low: id++,
5630 cutVertex: false
5631 };
5632 var edges = eles.getElementById(currentNode).connectedEdges().intersection(eles);
5633
5634 if (edges.size() === 0) {
5635 components.push(eles.spawn(eles.getElementById(currentNode)));
5636 } else {
5637 var sourceId, targetId, otherNodeId, edgeId;
5638 edges.forEach(function (edge) {
5639 sourceId = edge.source().id();
5640 targetId = edge.target().id();
5641 otherNodeId = sourceId === currentNode ? targetId : sourceId;
5642
5643 if (otherNodeId !== parent) {
5644 edgeId = edge.id();
5645
5646 if (!visitedEdges[edgeId]) {
5647 visitedEdges[edgeId] = true;
5648 stack.push({
5649 x: currentNode,
5650 y: otherNodeId,
5651 edge: edge
5652 });
5653 }
5654
5655 if (!(otherNodeId in nodes)) {
5656 biconnectedSearch(root, otherNodeId, currentNode);
5657 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].low);
5658
5659 if (nodes[currentNode].id <= nodes[otherNodeId].low) {
5660 nodes[currentNode].cutVertex = true;
5661 buildComponent(currentNode, otherNodeId);
5662 }
5663 } else {
5664 nodes[currentNode].low = Math.min(nodes[currentNode].low, nodes[otherNodeId].id);
5665 }
5666 }
5667 });
5668 }
5669 };
5670
5671 eles.forEach(function (ele) {
5672 if (ele.isNode()) {
5673 var nodeId = ele.id();
5674
5675 if (!(nodeId in nodes)) {
5676 edgeCount = 0;
5677 biconnectedSearch(nodeId, nodeId);
5678 nodes[nodeId].cutVertex = edgeCount > 1;
5679 }
5680 }
5681 });
5682 var cutVertices = Object.keys(nodes).filter(function (id) {
5683 return nodes[id].cutVertex;
5684 }).map(function (id) {
5685 return eles.getElementById(id);
5686 });
5687 return {
5688 cut: eles.spawn(cutVertices),
5689 components: components
5690 };
5691};
5692
5693var hopcroftTarjanBiconnected$1 = {
5694 hopcroftTarjanBiconnected: hopcroftTarjanBiconnected,
5695 htbc: hopcroftTarjanBiconnected,
5696 htb: hopcroftTarjanBiconnected,
5697 hopcroftTarjanBiconnectedComponents: hopcroftTarjanBiconnected
5698};
5699
5700var tarjanStronglyConnected = function tarjanStronglyConnected() {
5701 var eles = this;
5702 var nodes = {};
5703 var index = 0;
5704 var components = [];
5705 var stack = [];
5706 var cut = eles.spawn(eles);
5707
5708 var stronglyConnectedSearch = function stronglyConnectedSearch(sourceNodeId) {
5709 stack.push(sourceNodeId);
5710 nodes[sourceNodeId] = {
5711 index: index,
5712 low: index++,
5713 explored: false
5714 };
5715 var connectedEdges = eles.getElementById(sourceNodeId).connectedEdges().intersection(eles);
5716 connectedEdges.forEach(function (edge) {
5717 var targetNodeId = edge.target().id();
5718
5719 if (targetNodeId !== sourceNodeId) {
5720 if (!(targetNodeId in nodes)) {
5721 stronglyConnectedSearch(targetNodeId);
5722 }
5723
5724 if (!nodes[targetNodeId].explored) {
5725 nodes[sourceNodeId].low = Math.min(nodes[sourceNodeId].low, nodes[targetNodeId].low);
5726 }
5727 }
5728 });
5729
5730 if (nodes[sourceNodeId].index === nodes[sourceNodeId].low) {
5731 var componentNodes = eles.spawn();
5732
5733 for (;;) {
5734 var nodeId = stack.pop();
5735 componentNodes.merge(eles.getElementById(nodeId));
5736 nodes[nodeId].low = nodes[sourceNodeId].index;
5737 nodes[nodeId].explored = true;
5738
5739 if (nodeId === sourceNodeId) {
5740 break;
5741 }
5742 }
5743
5744 var componentEdges = componentNodes.edgesWith(componentNodes);
5745 var component = componentNodes.merge(componentEdges);
5746 components.push(component);
5747 cut = cut.difference(component);
5748 }
5749 };
5750
5751 eles.forEach(function (ele) {
5752 if (ele.isNode()) {
5753 var nodeId = ele.id();
5754
5755 if (!(nodeId in nodes)) {
5756 stronglyConnectedSearch(nodeId);
5757 }
5758 }
5759 });
5760 return {
5761 cut: cut,
5762 components: components
5763 };
5764};
5765
5766var tarjanStronglyConnected$1 = {
5767 tarjanStronglyConnected: tarjanStronglyConnected,
5768 tsc: tarjanStronglyConnected,
5769 tscc: tarjanStronglyConnected,
5770 tarjanStronglyConnectedComponents: tarjanStronglyConnected
5771};
5772
5773var elesfn$c = {};
5774[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) {
5775 extend(elesfn$c, props);
5776});
5777
5778/*!
5779Embeddable Minimum Strictly-Compliant Promises/A+ 1.1.1 Thenable
5780Copyright (c) 2013-2014 Ralf S. Engelschall (http://engelschall.com)
5781Licensed under The MIT License (http://opensource.org/licenses/MIT)
5782*/
5783
5784/* promise states [Promises/A+ 2.1] */
5785var STATE_PENDING = 0;
5786/* [Promises/A+ 2.1.1] */
5787
5788var STATE_FULFILLED = 1;
5789/* [Promises/A+ 2.1.2] */
5790
5791var STATE_REJECTED = 2;
5792/* [Promises/A+ 2.1.3] */
5793
5794/* promise object constructor */
5795
5796var api = function api(executor) {
5797 /* optionally support non-constructor/plain-function call */
5798 if (!(this instanceof api)) return new api(executor);
5799 /* initialize object */
5800
5801 this.id = 'Thenable/1.0.7';
5802 this.state = STATE_PENDING;
5803 /* initial state */
5804
5805 this.fulfillValue = undefined;
5806 /* initial value */
5807
5808 /* [Promises/A+ 1.3, 2.1.2.2] */
5809
5810 this.rejectReason = undefined;
5811 /* initial reason */
5812
5813 /* [Promises/A+ 1.5, 2.1.3.2] */
5814
5815 this.onFulfilled = [];
5816 /* initial handlers */
5817
5818 this.onRejected = [];
5819 /* initial handlers */
5820
5821 /* provide optional information-hiding proxy */
5822
5823 this.proxy = {
5824 then: this.then.bind(this)
5825 };
5826 /* support optional executor function */
5827
5828 if (typeof executor === 'function') executor.call(this, this.fulfill.bind(this), this.reject.bind(this));
5829};
5830/* promise API methods */
5831
5832
5833api.prototype = {
5834 /* promise resolving methods */
5835 fulfill: function fulfill(value) {
5836 return deliver(this, STATE_FULFILLED, 'fulfillValue', value);
5837 },
5838 reject: function reject(value) {
5839 return deliver(this, STATE_REJECTED, 'rejectReason', value);
5840 },
5841
5842 /* "The then Method" [Promises/A+ 1.1, 1.2, 2.2] */
5843 then: function then(onFulfilled, onRejected) {
5844 var curr = this;
5845 var next = new api();
5846 /* [Promises/A+ 2.2.7] */
5847
5848 curr.onFulfilled.push(resolver(onFulfilled, next, 'fulfill'));
5849 /* [Promises/A+ 2.2.2/2.2.6] */
5850
5851 curr.onRejected.push(resolver(onRejected, next, 'reject'));
5852 /* [Promises/A+ 2.2.3/2.2.6] */
5853
5854 execute(curr);
5855 return next.proxy;
5856 /* [Promises/A+ 2.2.7, 3.3] */
5857 }
5858};
5859/* deliver an action */
5860
5861var deliver = function deliver(curr, state, name, value) {
5862 if (curr.state === STATE_PENDING) {
5863 curr.state = state;
5864 /* [Promises/A+ 2.1.2.1, 2.1.3.1] */
5865
5866 curr[name] = value;
5867 /* [Promises/A+ 2.1.2.2, 2.1.3.2] */
5868
5869 execute(curr);
5870 }
5871
5872 return curr;
5873};
5874/* execute all handlers */
5875
5876
5877var execute = function execute(curr) {
5878 if (curr.state === STATE_FULFILLED) execute_handlers(curr, 'onFulfilled', curr.fulfillValue);else if (curr.state === STATE_REJECTED) execute_handlers(curr, 'onRejected', curr.rejectReason);
5879};
5880/* execute particular set of handlers */
5881
5882
5883var execute_handlers = function execute_handlers(curr, name, value) {
5884 /* global setImmediate: true */
5885
5886 /* global setTimeout: true */
5887
5888 /* short-circuit processing */
5889 if (curr[name].length === 0) return;
5890 /* iterate over all handlers, exactly once */
5891
5892 var handlers = curr[name];
5893 curr[name] = [];
5894 /* [Promises/A+ 2.2.2.3, 2.2.3.3] */
5895
5896 var func = function func() {
5897 for (var i = 0; i < handlers.length; i++) {
5898 handlers[i](value);
5899 }
5900 /* [Promises/A+ 2.2.5] */
5901
5902 };
5903 /* execute procedure asynchronously */
5904
5905 /* [Promises/A+ 2.2.4, 3.1] */
5906
5907
5908 if (typeof setImmediate === 'function') setImmediate(func);else setTimeout(func, 0);
5909};
5910/* generate a resolver function */
5911
5912
5913var resolver = function resolver(cb, next, method) {
5914 return function (value) {
5915 if (typeof cb !== 'function')
5916 /* [Promises/A+ 2.2.1, 2.2.7.3, 2.2.7.4] */
5917 next[method].call(next, value);
5918 /* [Promises/A+ 2.2.7.3, 2.2.7.4] */
5919 else {
5920 var result;
5921
5922 try {
5923 result = cb(value);
5924 }
5925 /* [Promises/A+ 2.2.2.1, 2.2.3.1, 2.2.5, 3.2] */
5926 catch (e) {
5927 next.reject(e);
5928 /* [Promises/A+ 2.2.7.2] */
5929
5930 return;
5931 }
5932
5933 resolve(next, result);
5934 /* [Promises/A+ 2.2.7.1] */
5935 }
5936 };
5937};
5938/* "Promise Resolution Procedure" */
5939
5940/* [Promises/A+ 2.3] */
5941
5942
5943var resolve = function resolve(promise, x) {
5944 /* sanity check arguments */
5945
5946 /* [Promises/A+ 2.3.1] */
5947 if (promise === x || promise.proxy === x) {
5948 promise.reject(new TypeError('cannot resolve promise with itself'));
5949 return;
5950 }
5951 /* surgically check for a "then" method
5952 (mainly to just call the "getter" of "then" only once) */
5953
5954
5955 var then;
5956
5957 if (_typeof(x) === 'object' && x !== null || typeof x === 'function') {
5958 try {
5959 then = x.then;
5960 }
5961 /* [Promises/A+ 2.3.3.1, 3.5] */
5962 catch (e) {
5963 promise.reject(e);
5964 /* [Promises/A+ 2.3.3.2] */
5965
5966 return;
5967 }
5968 }
5969 /* handle own Thenables [Promises/A+ 2.3.2]
5970 and similar "thenables" [Promises/A+ 2.3.3] */
5971
5972
5973 if (typeof then === 'function') {
5974 var resolved = false;
5975
5976 try {
5977 /* call retrieved "then" method */
5978
5979 /* [Promises/A+ 2.3.3.3] */
5980 then.call(x,
5981 /* resolvePromise */
5982
5983 /* [Promises/A+ 2.3.3.3.1] */
5984 function (y) {
5985 if (resolved) return;
5986 resolved = true;
5987 /* [Promises/A+ 2.3.3.3.3] */
5988
5989 if (y === x)
5990 /* [Promises/A+ 3.6] */
5991 promise.reject(new TypeError('circular thenable chain'));else resolve(promise, y);
5992 },
5993 /* rejectPromise */
5994
5995 /* [Promises/A+ 2.3.3.3.2] */
5996 function (r) {
5997 if (resolved) return;
5998 resolved = true;
5999 /* [Promises/A+ 2.3.3.3.3] */
6000
6001 promise.reject(r);
6002 });
6003 } catch (e) {
6004 if (!resolved)
6005 /* [Promises/A+ 2.3.3.3.3] */
6006 promise.reject(e);
6007 /* [Promises/A+ 2.3.3.3.4] */
6008 }
6009
6010 return;
6011 }
6012 /* handle other values */
6013
6014
6015 promise.fulfill(x);
6016 /* [Promises/A+ 2.3.4, 2.3.3.4] */
6017}; // so we always have Promise.all()
6018
6019
6020api.all = function (ps) {
6021 return new api(function (resolveAll, rejectAll) {
6022 var vals = new Array(ps.length);
6023 var doneCount = 0;
6024
6025 var fulfill = function fulfill(i, val) {
6026 vals[i] = val;
6027 doneCount++;
6028
6029 if (doneCount === ps.length) {
6030 resolveAll(vals);
6031 }
6032 };
6033
6034 for (var i = 0; i < ps.length; i++) {
6035 (function (i) {
6036 var p = ps[i];
6037 var isPromise = p != null && p.then != null;
6038
6039 if (isPromise) {
6040 p.then(function (val) {
6041 fulfill(i, val);
6042 }, function (err) {
6043 rejectAll(err);
6044 });
6045 } else {
6046 var val = p;
6047 fulfill(i, val);
6048 }
6049 })(i);
6050 }
6051 });
6052};
6053
6054api.resolve = function (val) {
6055 return new api(function (resolve, reject) {
6056 resolve(val);
6057 });
6058};
6059
6060api.reject = function (val) {
6061 return new api(function (resolve, reject) {
6062 reject(val);
6063 });
6064};
6065
6066var Promise$1 = typeof Promise !== 'undefined' ? Promise : api; // eslint-disable-line no-undef
6067
6068var Animation = function Animation(target, opts, opts2) {
6069 var isCore = core(target);
6070 var isEle = !isCore;
6071
6072 var _p = this._private = extend({
6073 duration: 1000
6074 }, opts, opts2);
6075
6076 _p.target = target;
6077 _p.style = _p.style || _p.css;
6078 _p.started = false;
6079 _p.playing = false;
6080 _p.hooked = false;
6081 _p.applying = false;
6082 _p.progress = 0;
6083 _p.completes = [];
6084 _p.frames = [];
6085
6086 if (_p.complete && fn(_p.complete)) {
6087 _p.completes.push(_p.complete);
6088 }
6089
6090 if (isEle) {
6091 var pos = target.position();
6092 _p.startPosition = _p.startPosition || {
6093 x: pos.x,
6094 y: pos.y
6095 };
6096 _p.startStyle = _p.startStyle || target.cy().style().getAnimationStartStyle(target, _p.style);
6097 }
6098
6099 if (isCore) {
6100 var pan = target.pan();
6101 _p.startPan = {
6102 x: pan.x,
6103 y: pan.y
6104 };
6105 _p.startZoom = target.zoom();
6106 } // for future timeline/animations impl
6107
6108
6109 this.length = 1;
6110 this[0] = this;
6111};
6112
6113var anifn = Animation.prototype;
6114extend(anifn, {
6115 instanceString: function instanceString() {
6116 return 'animation';
6117 },
6118 hook: function hook() {
6119 var _p = this._private;
6120
6121 if (!_p.hooked) {
6122 // add to target's animation queue
6123 var q;
6124 var tAni = _p.target._private.animation;
6125
6126 if (_p.queue) {
6127 q = tAni.queue;
6128 } else {
6129 q = tAni.current;
6130 }
6131
6132 q.push(this); // add to the animation loop pool
6133
6134 if (elementOrCollection(_p.target)) {
6135 _p.target.cy().addToAnimationPool(_p.target);
6136 }
6137
6138 _p.hooked = true;
6139 }
6140
6141 return this;
6142 },
6143 play: function play() {
6144 var _p = this._private; // autorewind
6145
6146 if (_p.progress === 1) {
6147 _p.progress = 0;
6148 }
6149
6150 _p.playing = true;
6151 _p.started = false; // needs to be started by animation loop
6152
6153 _p.stopped = false;
6154 this.hook(); // the animation loop will start the animation...
6155
6156 return this;
6157 },
6158 playing: function playing() {
6159 return this._private.playing;
6160 },
6161 apply: function apply() {
6162 var _p = this._private;
6163 _p.applying = true;
6164 _p.started = false; // needs to be started by animation loop
6165
6166 _p.stopped = false;
6167 this.hook(); // the animation loop will apply the animation at this progress
6168
6169 return this;
6170 },
6171 applying: function applying() {
6172 return this._private.applying;
6173 },
6174 pause: function pause() {
6175 var _p = this._private;
6176 _p.playing = false;
6177 _p.started = false;
6178 return this;
6179 },
6180 stop: function stop() {
6181 var _p = this._private;
6182 _p.playing = false;
6183 _p.started = false;
6184 _p.stopped = true; // to be removed from animation queues
6185
6186 return this;
6187 },
6188 rewind: function rewind() {
6189 return this.progress(0);
6190 },
6191 fastforward: function fastforward() {
6192 return this.progress(1);
6193 },
6194 time: function time(t) {
6195 var _p = this._private;
6196
6197 if (t === undefined) {
6198 return _p.progress * _p.duration;
6199 } else {
6200 return this.progress(t / _p.duration);
6201 }
6202 },
6203 progress: function progress(p) {
6204 var _p = this._private;
6205 var wasPlaying = _p.playing;
6206
6207 if (p === undefined) {
6208 return _p.progress;
6209 } else {
6210 if (wasPlaying) {
6211 this.pause();
6212 }
6213
6214 _p.progress = p;
6215 _p.started = false;
6216
6217 if (wasPlaying) {
6218 this.play();
6219 }
6220 }
6221
6222 return this;
6223 },
6224 completed: function completed() {
6225 return this._private.progress === 1;
6226 },
6227 reverse: function reverse() {
6228 var _p = this._private;
6229 var wasPlaying = _p.playing;
6230
6231 if (wasPlaying) {
6232 this.pause();
6233 }
6234
6235 _p.progress = 1 - _p.progress;
6236 _p.started = false;
6237
6238 var swap = function swap(a, b) {
6239 var _pa = _p[a];
6240
6241 if (_pa == null) {
6242 return;
6243 }
6244
6245 _p[a] = _p[b];
6246 _p[b] = _pa;
6247 };
6248
6249 swap('zoom', 'startZoom');
6250 swap('pan', 'startPan');
6251 swap('position', 'startPosition'); // swap styles
6252
6253 if (_p.style) {
6254 for (var i = 0; i < _p.style.length; i++) {
6255 var prop = _p.style[i];
6256 var name = prop.name;
6257 var startStyleProp = _p.startStyle[name];
6258 _p.startStyle[name] = prop;
6259 _p.style[i] = startStyleProp;
6260 }
6261 }
6262
6263 if (wasPlaying) {
6264 this.play();
6265 }
6266
6267 return this;
6268 },
6269 promise: function promise(type) {
6270 var _p = this._private;
6271 var arr;
6272
6273 switch (type) {
6274 case 'frame':
6275 arr = _p.frames;
6276 break;
6277
6278 default:
6279 case 'complete':
6280 case 'completed':
6281 arr = _p.completes;
6282 }
6283
6284 return new Promise$1(function (resolve, reject) {
6285 arr.push(function () {
6286 resolve();
6287 });
6288 });
6289 }
6290});
6291anifn.complete = anifn.completed;
6292anifn.run = anifn.play;
6293anifn.running = anifn.playing;
6294
6295var define = {
6296 animated: function animated() {
6297 return function animatedImpl() {
6298 var self = this;
6299 var selfIsArrayLike = self.length !== undefined;
6300 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6301
6302 var cy = this._private.cy || this;
6303
6304 if (!cy.styleEnabled()) {
6305 return false;
6306 }
6307
6308 var ele = all[0];
6309
6310 if (ele) {
6311 return ele._private.animation.current.length > 0;
6312 }
6313 };
6314 },
6315 // animated
6316 clearQueue: function clearQueue() {
6317 return function clearQueueImpl() {
6318 var self = this;
6319 var selfIsArrayLike = self.length !== undefined;
6320 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6321
6322 var cy = this._private.cy || this;
6323
6324 if (!cy.styleEnabled()) {
6325 return this;
6326 }
6327
6328 for (var i = 0; i < all.length; i++) {
6329 var ele = all[i];
6330 ele._private.animation.queue = [];
6331 }
6332
6333 return this;
6334 };
6335 },
6336 // clearQueue
6337 delay: function delay() {
6338 return function delayImpl(time, complete) {
6339 var cy = this._private.cy || this;
6340
6341 if (!cy.styleEnabled()) {
6342 return this;
6343 }
6344
6345 return this.animate({
6346 delay: time,
6347 duration: time,
6348 complete: complete
6349 });
6350 };
6351 },
6352 // delay
6353 delayAnimation: function delayAnimation() {
6354 return function delayAnimationImpl(time, complete) {
6355 var cy = this._private.cy || this;
6356
6357 if (!cy.styleEnabled()) {
6358 return this;
6359 }
6360
6361 return this.animation({
6362 delay: time,
6363 duration: time,
6364 complete: complete
6365 });
6366 };
6367 },
6368 // delay
6369 animation: function animation() {
6370 return function animationImpl(properties, params) {
6371 var self = this;
6372 var selfIsArrayLike = self.length !== undefined;
6373 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6374
6375 var cy = this._private.cy || this;
6376 var isCore = !selfIsArrayLike;
6377 var isEles = !isCore;
6378
6379 if (!cy.styleEnabled()) {
6380 return this;
6381 }
6382
6383 var style = cy.style();
6384 properties = extend({}, properties, params);
6385 var propertiesEmpty = Object.keys(properties).length === 0;
6386
6387 if (propertiesEmpty) {
6388 return new Animation(all[0], properties); // nothing to animate
6389 }
6390
6391 if (properties.duration === undefined) {
6392 properties.duration = 400;
6393 }
6394
6395 switch (properties.duration) {
6396 case 'slow':
6397 properties.duration = 600;
6398 break;
6399
6400 case 'fast':
6401 properties.duration = 200;
6402 break;
6403 }
6404
6405 if (isEles) {
6406 properties.style = style.getPropsList(properties.style || properties.css);
6407 properties.css = undefined;
6408 }
6409
6410 if (isEles && properties.renderedPosition != null) {
6411 var rpos = properties.renderedPosition;
6412 var pan = cy.pan();
6413 var zoom = cy.zoom();
6414 properties.position = renderedToModelPosition(rpos, zoom, pan);
6415 } // override pan w/ panBy if set
6416
6417
6418 if (isCore && properties.panBy != null) {
6419 var panBy = properties.panBy;
6420 var cyPan = cy.pan();
6421 properties.pan = {
6422 x: cyPan.x + panBy.x,
6423 y: cyPan.y + panBy.y
6424 };
6425 } // override pan w/ center if set
6426
6427
6428 var center = properties.center || properties.centre;
6429
6430 if (isCore && center != null) {
6431 var centerPan = cy.getCenterPan(center.eles, properties.zoom);
6432
6433 if (centerPan != null) {
6434 properties.pan = centerPan;
6435 }
6436 } // override pan & zoom w/ fit if set
6437
6438
6439 if (isCore && properties.fit != null) {
6440 var fit = properties.fit;
6441 var fitVp = cy.getFitViewport(fit.eles || fit.boundingBox, fit.padding);
6442
6443 if (fitVp != null) {
6444 properties.pan = fitVp.pan;
6445 properties.zoom = fitVp.zoom;
6446 }
6447 } // override zoom (& potentially pan) w/ zoom obj if set
6448
6449
6450 if (isCore && plainObject(properties.zoom)) {
6451 var vp = cy.getZoomedViewport(properties.zoom);
6452
6453 if (vp != null) {
6454 if (vp.zoomed) {
6455 properties.zoom = vp.zoom;
6456 }
6457
6458 if (vp.panned) {
6459 properties.pan = vp.pan;
6460 }
6461 } else {
6462 properties.zoom = null; // an inavalid zoom (e.g. no delta) gets automatically destroyed
6463 }
6464 }
6465
6466 return new Animation(all[0], properties);
6467 };
6468 },
6469 // animate
6470 animate: function animate() {
6471 return function animateImpl(properties, params) {
6472 var self = this;
6473 var selfIsArrayLike = self.length !== undefined;
6474 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6475
6476 var cy = this._private.cy || this;
6477
6478 if (!cy.styleEnabled()) {
6479 return this;
6480 }
6481
6482 if (params) {
6483 properties = extend({}, properties, params);
6484 } // manually hook and run the animation
6485
6486
6487 for (var i = 0; i < all.length; i++) {
6488 var ele = all[i];
6489 var queue = ele.animated() && (properties.queue === undefined || properties.queue);
6490 var ani = ele.animation(properties, queue ? {
6491 queue: true
6492 } : undefined);
6493 ani.play();
6494 }
6495
6496 return this; // chaining
6497 };
6498 },
6499 // animate
6500 stop: function stop() {
6501 return function stopImpl(clearQueue, jumpToEnd) {
6502 var self = this;
6503 var selfIsArrayLike = self.length !== undefined;
6504 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6505
6506 var cy = this._private.cy || this;
6507
6508 if (!cy.styleEnabled()) {
6509 return this;
6510 }
6511
6512 for (var i = 0; i < all.length; i++) {
6513 var ele = all[i];
6514 var _p = ele._private;
6515 var anis = _p.animation.current;
6516
6517 for (var j = 0; j < anis.length; j++) {
6518 var ani = anis[j];
6519 var ani_p = ani._private;
6520
6521 if (jumpToEnd) {
6522 // next iteration of the animation loop, the animation
6523 // will go straight to the end and be removed
6524 ani_p.duration = 0;
6525 }
6526 } // clear the queue of future animations
6527
6528
6529 if (clearQueue) {
6530 _p.animation.queue = [];
6531 }
6532
6533 if (!jumpToEnd) {
6534 _p.animation.current = [];
6535 }
6536 } // we have to notify (the animation loop doesn't do it for us on `stop`)
6537
6538
6539 cy.notify('draw');
6540 return this;
6541 };
6542 } // stop
6543
6544}; // define
6545
6546var define$1 = {
6547 // access data field
6548 data: function data(params) {
6549 var defaults = {
6550 field: 'data',
6551 bindingEvent: 'data',
6552 allowBinding: false,
6553 allowSetting: false,
6554 allowGetting: false,
6555 settingEvent: 'data',
6556 settingTriggersEvent: false,
6557 triggerFnName: 'trigger',
6558 immutableKeys: {},
6559 // key => true if immutable
6560 updateStyle: false,
6561 beforeGet: function beforeGet(self) {},
6562 beforeSet: function beforeSet(self, obj) {},
6563 onSet: function onSet(self) {},
6564 canSet: function canSet(self) {
6565 return true;
6566 }
6567 };
6568 params = extend({}, defaults, params);
6569 return function dataImpl(name, value) {
6570 var p = params;
6571 var self = this;
6572 var selfIsArrayLike = self.length !== undefined;
6573 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6574
6575 var single = selfIsArrayLike ? self[0] : self; // .data('foo', ...)
6576
6577 if (string(name)) {
6578 // set or get property
6579 // .data('foo')
6580 if (p.allowGetting && value === undefined) {
6581 // get
6582 var ret;
6583
6584 if (single) {
6585 p.beforeGet(single);
6586 ret = single._private[p.field][name];
6587 }
6588
6589 return ret; // .data('foo', 'bar')
6590 } else if (p.allowSetting && value !== undefined) {
6591 // set
6592 var valid = !p.immutableKeys[name];
6593
6594 if (valid) {
6595 var change = _defineProperty({}, name, value);
6596
6597 p.beforeSet(self, change);
6598
6599 for (var i = 0, l = all.length; i < l; i++) {
6600 var ele = all[i];
6601
6602 if (p.canSet(ele)) {
6603 ele._private[p.field][name] = value;
6604 }
6605 } // update mappers if asked
6606
6607
6608 if (p.updateStyle) {
6609 self.updateStyle();
6610 } // call onSet callback
6611
6612
6613 p.onSet(self);
6614
6615 if (p.settingTriggersEvent) {
6616 self[p.triggerFnName](p.settingEvent);
6617 }
6618 }
6619 } // .data({ 'foo': 'bar' })
6620
6621 } else if (p.allowSetting && plainObject(name)) {
6622 // extend
6623 var obj = name;
6624 var k, v;
6625 var keys = Object.keys(obj);
6626 p.beforeSet(self, obj);
6627
6628 for (var _i = 0; _i < keys.length; _i++) {
6629 k = keys[_i];
6630 v = obj[k];
6631
6632 var _valid = !p.immutableKeys[k];
6633
6634 if (_valid) {
6635 for (var j = 0; j < all.length; j++) {
6636 var _ele = all[j];
6637
6638 if (p.canSet(_ele)) {
6639 _ele._private[p.field][k] = v;
6640 }
6641 }
6642 }
6643 } // update mappers if asked
6644
6645
6646 if (p.updateStyle) {
6647 self.updateStyle();
6648 } // call onSet callback
6649
6650
6651 p.onSet(self);
6652
6653 if (p.settingTriggersEvent) {
6654 self[p.triggerFnName](p.settingEvent);
6655 } // .data(function(){ ... })
6656
6657 } else if (p.allowBinding && fn(name)) {
6658 // bind to event
6659 var fn$1 = name;
6660 self.on(p.bindingEvent, fn$1); // .data()
6661 } else if (p.allowGetting && name === undefined) {
6662 // get whole object
6663 var _ret;
6664
6665 if (single) {
6666 p.beforeGet(single);
6667 _ret = single._private[p.field];
6668 }
6669
6670 return _ret;
6671 }
6672
6673 return self; // maintain chainability
6674 }; // function
6675 },
6676 // data
6677 // remove data field
6678 removeData: function removeData(params) {
6679 var defaults = {
6680 field: 'data',
6681 event: 'data',
6682 triggerFnName: 'trigger',
6683 triggerEvent: false,
6684 immutableKeys: {} // key => true if immutable
6685
6686 };
6687 params = extend({}, defaults, params);
6688 return function removeDataImpl(names) {
6689 var p = params;
6690 var self = this;
6691 var selfIsArrayLike = self.length !== undefined;
6692 var all = selfIsArrayLike ? self : [self]; // put in array if not array-like
6693 // .removeData('foo bar')
6694
6695 if (string(names)) {
6696 // then get the list of keys, and delete them
6697 var keys = names.split(/\s+/);
6698 var l = keys.length;
6699
6700 for (var i = 0; i < l; i++) {
6701 // delete each non-empty key
6702 var key = keys[i];
6703
6704 if (emptyString(key)) {
6705 continue;
6706 }
6707
6708 var valid = !p.immutableKeys[key]; // not valid if immutable
6709
6710 if (valid) {
6711 for (var i_a = 0, l_a = all.length; i_a < l_a; i_a++) {
6712 all[i_a]._private[p.field][key] = undefined;
6713 }
6714 }
6715 }
6716
6717 if (p.triggerEvent) {
6718 self[p.triggerFnName](p.event);
6719 } // .removeData()
6720
6721 } else if (names === undefined) {
6722 // then delete all keys
6723 for (var _i_a = 0, _l_a = all.length; _i_a < _l_a; _i_a++) {
6724 var _privateFields = all[_i_a]._private[p.field];
6725
6726 var _keys = Object.keys(_privateFields);
6727
6728 for (var _i2 = 0; _i2 < _keys.length; _i2++) {
6729 var _key = _keys[_i2];
6730 var validKeyToDelete = !p.immutableKeys[_key];
6731
6732 if (validKeyToDelete) {
6733 _privateFields[_key] = undefined;
6734 }
6735 }
6736 }
6737
6738 if (p.triggerEvent) {
6739 self[p.triggerFnName](p.event);
6740 }
6741 }
6742
6743 return self; // maintain chaining
6744 }; // function
6745 } // removeData
6746
6747}; // define
6748
6749var define$2 = {
6750 eventAliasesOn: function eventAliasesOn(proto) {
6751 var p = proto;
6752 p.addListener = p.listen = p.bind = p.on;
6753 p.unlisten = p.unbind = p.off = p.removeListener;
6754 p.trigger = p.emit; // this is just a wrapper alias of .on()
6755
6756 p.pon = p.promiseOn = function (events, selector) {
6757 var self = this;
6758 var args = Array.prototype.slice.call(arguments, 0);
6759 return new Promise$1(function (resolve, reject) {
6760 var callback = function callback(e) {
6761 self.off.apply(self, offArgs);
6762 resolve(e);
6763 };
6764
6765 var onArgs = args.concat([callback]);
6766 var offArgs = onArgs.concat([]);
6767 self.on.apply(self, onArgs);
6768 });
6769 };
6770 }
6771}; // define
6772
6773// use this module to cherry pick functions into your prototype
6774var define$3 = {};
6775[define, define$1, define$2].forEach(function (m) {
6776 extend(define$3, m);
6777});
6778
6779var elesfn$d = {
6780 animate: define$3.animate(),
6781 animation: define$3.animation(),
6782 animated: define$3.animated(),
6783 clearQueue: define$3.clearQueue(),
6784 delay: define$3.delay(),
6785 delayAnimation: define$3.delayAnimation(),
6786 stop: define$3.stop()
6787};
6788
6789var elesfn$e = {
6790 classes: function classes(_classes) {
6791 var self = this;
6792
6793 if (_classes === undefined) {
6794 var ret = [];
6795
6796 self[0]._private.classes.forEach(function (cls) {
6797 return ret.push(cls);
6798 });
6799
6800 return ret;
6801 } else if (!array(_classes)) {
6802 // extract classes from string
6803 _classes = (_classes || '').match(/\S+/g) || [];
6804 }
6805
6806 var changed = [];
6807 var classesSet = new Set$1(_classes); // check and update each ele
6808
6809 for (var j = 0; j < self.length; j++) {
6810 var ele = self[j];
6811 var _p = ele._private;
6812 var eleClasses = _p.classes;
6813 var changedEle = false; // check if ele has all of the passed classes
6814
6815 for (var i = 0; i < _classes.length; i++) {
6816 var cls = _classes[i];
6817 var eleHasClass = eleClasses.has(cls);
6818
6819 if (!eleHasClass) {
6820 changedEle = true;
6821 break;
6822 }
6823 } // check if ele has classes outside of those passed
6824
6825
6826 if (!changedEle) {
6827 changedEle = eleClasses.size !== _classes.length;
6828 }
6829
6830 if (changedEle) {
6831 _p.classes = classesSet;
6832 changed.push(ele);
6833 }
6834 } // trigger update style on those eles that had class changes
6835
6836
6837 if (changed.length > 0) {
6838 this.spawn(changed).updateStyle().emit('class');
6839 }
6840
6841 return self;
6842 },
6843 addClass: function addClass(classes) {
6844 return this.toggleClass(classes, true);
6845 },
6846 hasClass: function hasClass(className) {
6847 var ele = this[0];
6848 return ele != null && ele._private.classes.has(className);
6849 },
6850 toggleClass: function toggleClass(classes, toggle) {
6851 if (!array(classes)) {
6852 // extract classes from string
6853 classes = classes.match(/\S+/g) || [];
6854 }
6855
6856 var self = this;
6857 var toggleUndefd = toggle === undefined;
6858 var changed = []; // eles who had classes changed
6859
6860 for (var i = 0, il = self.length; i < il; i++) {
6861 var ele = self[i];
6862 var eleClasses = ele._private.classes;
6863 var changedEle = false;
6864
6865 for (var j = 0; j < classes.length; j++) {
6866 var cls = classes[j];
6867 var hasClass = eleClasses.has(cls);
6868 var changedNow = false;
6869
6870 if (toggle || toggleUndefd && !hasClass) {
6871 eleClasses.add(cls);
6872 changedNow = true;
6873 } else if (!toggle || toggleUndefd && hasClass) {
6874 eleClasses["delete"](cls);
6875 changedNow = true;
6876 }
6877
6878 if (!changedEle && changedNow) {
6879 changed.push(ele);
6880 changedEle = true;
6881 }
6882 } // for j classes
6883
6884 } // for i eles
6885 // trigger update style on those eles that had class changes
6886
6887
6888 if (changed.length > 0) {
6889 this.spawn(changed).updateStyle().emit('class');
6890 }
6891
6892 return self;
6893 },
6894 removeClass: function removeClass(classes) {
6895 return this.toggleClass(classes, false);
6896 },
6897 flashClass: function flashClass(classes, duration) {
6898 var self = this;
6899
6900 if (duration == null) {
6901 duration = 250;
6902 } else if (duration === 0) {
6903 return self; // nothing to do really
6904 }
6905
6906 self.addClass(classes);
6907 setTimeout(function () {
6908 self.removeClass(classes);
6909 }, duration);
6910 return self;
6911 }
6912};
6913elesfn$e.className = elesfn$e.classNames = elesfn$e.classes;
6914
6915var tokens = {
6916 metaChar: '[\\!\\"\\#\\$\\%\\&\\\'\\(\\)\\*\\+\\,\\.\\/\\:\\;\\<\\=\\>\\?\\@\\[\\]\\^\\`\\{\\|\\}\\~]',
6917 // chars we need to escape in let names, etc
6918 comparatorOp: '=|\\!=|>|>=|<|<=|\\$=|\\^=|\\*=',
6919 // binary comparison op (used in data selectors)
6920 boolOp: '\\?|\\!|\\^',
6921 // boolean (unary) operators (used in data selectors)
6922 string: '"(?:\\\\"|[^"])*"' + '|' + "'(?:\\\\'|[^'])*'",
6923 // string literals (used in data selectors) -- doublequotes | singlequotes
6924 number: number$1,
6925 // number literal (used in data selectors) --- e.g. 0.1234, 1234, 12e123
6926 meta: 'degree|indegree|outdegree',
6927 // allowed metadata fields (i.e. allowed functions to use from Collection)
6928 separator: '\\s*,\\s*',
6929 // queries are separated by commas, e.g. edge[foo = 'bar'], node.someClass
6930 descendant: '\\s+',
6931 child: '\\s+>\\s+',
6932 subject: '\\$',
6933 group: 'node|edge|\\*',
6934 directedEdge: '\\s+->\\s+',
6935 undirectedEdge: '\\s+<->\\s+'
6936};
6937tokens.variable = '(?:[\\w-]|(?:\\\\' + tokens.metaChar + '))+'; // a variable name
6938
6939tokens.value = tokens.string + '|' + tokens.number; // a value literal, either a string or number
6940
6941tokens.className = tokens.variable; // a class name (follows variable conventions)
6942
6943tokens.id = tokens.variable; // an element id (follows variable conventions)
6944
6945(function () {
6946 var ops, op, i; // add @ variants to comparatorOp
6947
6948 ops = tokens.comparatorOp.split('|');
6949
6950 for (i = 0; i < ops.length; i++) {
6951 op = ops[i];
6952 tokens.comparatorOp += '|@' + op;
6953 } // add ! variants to comparatorOp
6954
6955
6956 ops = tokens.comparatorOp.split('|');
6957
6958 for (i = 0; i < ops.length; i++) {
6959 op = ops[i];
6960
6961 if (op.indexOf('!') >= 0) {
6962 continue;
6963 } // skip ops that explicitly contain !
6964
6965
6966 if (op === '=') {
6967 continue;
6968 } // skip = b/c != is explicitly defined
6969
6970
6971 tokens.comparatorOp += '|\\!' + op;
6972 }
6973})();
6974
6975/**
6976 * Make a new query object
6977 *
6978 * @prop type {Type} The type enum (int) of the query
6979 * @prop checks List of checks to make against an ele to test for a match
6980 */
6981var newQuery = function newQuery() {
6982 return {
6983 checks: []
6984 };
6985};
6986
6987/**
6988 * A check type enum-like object. Uses integer values for fast match() lookup.
6989 * The ordering does not matter as long as the ints are unique.
6990 */
6991var Type = {
6992 /** E.g. node */
6993 GROUP: 0,
6994
6995 /** A collection of elements */
6996 COLLECTION: 1,
6997
6998 /** A filter(ele) function */
6999 FILTER: 2,
7000
7001 /** E.g. [foo > 1] */
7002 DATA_COMPARE: 3,
7003
7004 /** E.g. [foo] */
7005 DATA_EXIST: 4,
7006
7007 /** E.g. [?foo] */
7008 DATA_BOOL: 5,
7009
7010 /** E.g. [[degree > 2]] */
7011 META_COMPARE: 6,
7012
7013 /** E.g. :selected */
7014 STATE: 7,
7015
7016 /** E.g. #foo */
7017 ID: 8,
7018
7019 /** E.g. .foo */
7020 CLASS: 9,
7021
7022 /** E.g. #foo <-> #bar */
7023 UNDIRECTED_EDGE: 10,
7024
7025 /** E.g. #foo -> #bar */
7026 DIRECTED_EDGE: 11,
7027
7028 /** E.g. $#foo -> #bar */
7029 NODE_SOURCE: 12,
7030
7031 /** E.g. #foo -> $#bar */
7032 NODE_TARGET: 13,
7033
7034 /** E.g. $#foo <-> #bar */
7035 NODE_NEIGHBOR: 14,
7036
7037 /** E.g. #foo > #bar */
7038 CHILD: 15,
7039
7040 /** E.g. #foo #bar */
7041 DESCENDANT: 16,
7042
7043 /** E.g. $#foo > #bar */
7044 PARENT: 17,
7045
7046 /** E.g. $#foo #bar */
7047 ANCESTOR: 18,
7048
7049 /** E.g. #foo > $bar > #baz */
7050 COMPOUND_SPLIT: 19,
7051
7052 /** Always matches, useful placeholder for subject in `COMPOUND_SPLIT` */
7053 TRUE: 20
7054};
7055
7056var stateSelectors = [{
7057 selector: ':selected',
7058 matches: function matches(ele) {
7059 return ele.selected();
7060 }
7061}, {
7062 selector: ':unselected',
7063 matches: function matches(ele) {
7064 return !ele.selected();
7065 }
7066}, {
7067 selector: ':selectable',
7068 matches: function matches(ele) {
7069 return ele.selectable();
7070 }
7071}, {
7072 selector: ':unselectable',
7073 matches: function matches(ele) {
7074 return !ele.selectable();
7075 }
7076}, {
7077 selector: ':locked',
7078 matches: function matches(ele) {
7079 return ele.locked();
7080 }
7081}, {
7082 selector: ':unlocked',
7083 matches: function matches(ele) {
7084 return !ele.locked();
7085 }
7086}, {
7087 selector: ':visible',
7088 matches: function matches(ele) {
7089 return ele.visible();
7090 }
7091}, {
7092 selector: ':hidden',
7093 matches: function matches(ele) {
7094 return !ele.visible();
7095 }
7096}, {
7097 selector: ':transparent',
7098 matches: function matches(ele) {
7099 return ele.transparent();
7100 }
7101}, {
7102 selector: ':grabbed',
7103 matches: function matches(ele) {
7104 return ele.grabbed();
7105 }
7106}, {
7107 selector: ':free',
7108 matches: function matches(ele) {
7109 return !ele.grabbed();
7110 }
7111}, {
7112 selector: ':removed',
7113 matches: function matches(ele) {
7114 return ele.removed();
7115 }
7116}, {
7117 selector: ':inside',
7118 matches: function matches(ele) {
7119 return !ele.removed();
7120 }
7121}, {
7122 selector: ':grabbable',
7123 matches: function matches(ele) {
7124 return ele.grabbable();
7125 }
7126}, {
7127 selector: ':ungrabbable',
7128 matches: function matches(ele) {
7129 return !ele.grabbable();
7130 }
7131}, {
7132 selector: ':animated',
7133 matches: function matches(ele) {
7134 return ele.animated();
7135 }
7136}, {
7137 selector: ':unanimated',
7138 matches: function matches(ele) {
7139 return !ele.animated();
7140 }
7141}, {
7142 selector: ':parent',
7143 matches: function matches(ele) {
7144 return ele.isParent();
7145 }
7146}, {
7147 selector: ':childless',
7148 matches: function matches(ele) {
7149 return ele.isChildless();
7150 }
7151}, {
7152 selector: ':child',
7153 matches: function matches(ele) {
7154 return ele.isChild();
7155 }
7156}, {
7157 selector: ':orphan',
7158 matches: function matches(ele) {
7159 return ele.isOrphan();
7160 }
7161}, {
7162 selector: ':nonorphan',
7163 matches: function matches(ele) {
7164 return ele.isChild();
7165 }
7166}, {
7167 selector: ':compound',
7168 matches: function matches(ele) {
7169 if (ele.isNode()) {
7170 return ele.isParent();
7171 } else {
7172 return ele.source().isParent() || ele.target().isParent();
7173 }
7174 }
7175}, {
7176 selector: ':loop',
7177 matches: function matches(ele) {
7178 return ele.isLoop();
7179 }
7180}, {
7181 selector: ':simple',
7182 matches: function matches(ele) {
7183 return ele.isSimple();
7184 }
7185}, {
7186 selector: ':active',
7187 matches: function matches(ele) {
7188 return ele.active();
7189 }
7190}, {
7191 selector: ':inactive',
7192 matches: function matches(ele) {
7193 return !ele.active();
7194 }
7195}, {
7196 selector: ':backgrounding',
7197 matches: function matches(ele) {
7198 return ele.backgrounding();
7199 }
7200}, {
7201 selector: ':nonbackgrounding',
7202 matches: function matches(ele) {
7203 return !ele.backgrounding();
7204 }
7205}].sort(function (a, b) {
7206 // n.b. selectors that are starting substrings of others must have the longer ones first
7207 return descending(a.selector, b.selector);
7208});
7209
7210var lookup = function () {
7211 var selToFn = {};
7212 var s;
7213
7214 for (var i = 0; i < stateSelectors.length; i++) {
7215 s = stateSelectors[i];
7216 selToFn[s.selector] = s.matches;
7217 }
7218
7219 return selToFn;
7220}();
7221
7222var stateSelectorMatches = function stateSelectorMatches(sel, ele) {
7223 return lookup[sel](ele);
7224};
7225var stateSelectorRegex = '(' + stateSelectors.map(function (s) {
7226 return s.selector;
7227}).join('|') + ')';
7228
7229// so that values get compared properly in Selector.filter()
7230
7231var cleanMetaChars = function cleanMetaChars(str) {
7232 return str.replace(new RegExp('\\\\(' + tokens.metaChar + ')', 'g'), function (match, $1) {
7233 return $1;
7234 });
7235};
7236
7237var replaceLastQuery = function replaceLastQuery(selector, examiningQuery, replacementQuery) {
7238 selector[selector.length - 1] = replacementQuery;
7239}; // NOTE: add new expression syntax here to have it recognised by the parser;
7240// - a query contains all adjacent (i.e. no separator in between) expressions;
7241// - the current query is stored in selector[i]
7242// - you need to check the query objects in match() for it actually filter properly, but that's pretty straight forward
7243
7244
7245var exprs = [{
7246 name: 'group',
7247 // just used for identifying when debugging
7248 query: true,
7249 regex: '(' + tokens.group + ')',
7250 populate: function populate(selector, query, _ref) {
7251 var _ref2 = _slicedToArray(_ref, 1),
7252 group = _ref2[0];
7253
7254 query.checks.push({
7255 type: Type.GROUP,
7256 value: group === '*' ? group : group + 's'
7257 });
7258 }
7259}, {
7260 name: 'state',
7261 query: true,
7262 regex: stateSelectorRegex,
7263 populate: function populate(selector, query, _ref3) {
7264 var _ref4 = _slicedToArray(_ref3, 1),
7265 state = _ref4[0];
7266
7267 query.checks.push({
7268 type: Type.STATE,
7269 value: state
7270 });
7271 }
7272}, {
7273 name: 'id',
7274 query: true,
7275 regex: '\\#(' + tokens.id + ')',
7276 populate: function populate(selector, query, _ref5) {
7277 var _ref6 = _slicedToArray(_ref5, 1),
7278 id = _ref6[0];
7279
7280 query.checks.push({
7281 type: Type.ID,
7282 value: cleanMetaChars(id)
7283 });
7284 }
7285}, {
7286 name: 'className',
7287 query: true,
7288 regex: '\\.(' + tokens.className + ')',
7289 populate: function populate(selector, query, _ref7) {
7290 var _ref8 = _slicedToArray(_ref7, 1),
7291 className = _ref8[0];
7292
7293 query.checks.push({
7294 type: Type.CLASS,
7295 value: cleanMetaChars(className)
7296 });
7297 }
7298}, {
7299 name: 'dataExists',
7300 query: true,
7301 regex: '\\[\\s*(' + tokens.variable + ')\\s*\\]',
7302 populate: function populate(selector, query, _ref9) {
7303 var _ref10 = _slicedToArray(_ref9, 1),
7304 variable = _ref10[0];
7305
7306 query.checks.push({
7307 type: Type.DATA_EXIST,
7308 field: cleanMetaChars(variable)
7309 });
7310 }
7311}, {
7312 name: 'dataCompare',
7313 query: true,
7314 regex: '\\[\\s*(' + tokens.variable + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.value + ')\\s*\\]',
7315 populate: function populate(selector, query, _ref11) {
7316 var _ref12 = _slicedToArray(_ref11, 3),
7317 variable = _ref12[0],
7318 comparatorOp = _ref12[1],
7319 value = _ref12[2];
7320
7321 var valueIsString = new RegExp('^' + tokens.string + '$').exec(value) != null;
7322
7323 if (valueIsString) {
7324 value = value.substring(1, value.length - 1);
7325 } else {
7326 value = parseFloat(value);
7327 }
7328
7329 query.checks.push({
7330 type: Type.DATA_COMPARE,
7331 field: cleanMetaChars(variable),
7332 operator: comparatorOp,
7333 value: value
7334 });
7335 }
7336}, {
7337 name: 'dataBool',
7338 query: true,
7339 regex: '\\[\\s*(' + tokens.boolOp + ')\\s*(' + tokens.variable + ')\\s*\\]',
7340 populate: function populate(selector, query, _ref13) {
7341 var _ref14 = _slicedToArray(_ref13, 2),
7342 boolOp = _ref14[0],
7343 variable = _ref14[1];
7344
7345 query.checks.push({
7346 type: Type.DATA_BOOL,
7347 field: cleanMetaChars(variable),
7348 operator: boolOp
7349 });
7350 }
7351}, {
7352 name: 'metaCompare',
7353 query: true,
7354 regex: '\\[\\[\\s*(' + tokens.meta + ')\\s*(' + tokens.comparatorOp + ')\\s*(' + tokens.number + ')\\s*\\]\\]',
7355 populate: function populate(selector, query, _ref15) {
7356 var _ref16 = _slicedToArray(_ref15, 3),
7357 meta = _ref16[0],
7358 comparatorOp = _ref16[1],
7359 number = _ref16[2];
7360
7361 query.checks.push({
7362 type: Type.META_COMPARE,
7363 field: cleanMetaChars(meta),
7364 operator: comparatorOp,
7365 value: parseFloat(number)
7366 });
7367 }
7368}, {
7369 name: 'nextQuery',
7370 separator: true,
7371 regex: tokens.separator,
7372 populate: function populate(selector, query) {
7373 var currentSubject = selector.currentSubject;
7374 var edgeCount = selector.edgeCount;
7375 var compoundCount = selector.compoundCount;
7376 var lastQ = selector[selector.length - 1];
7377
7378 if (currentSubject != null) {
7379 lastQ.subject = currentSubject;
7380 selector.currentSubject = null;
7381 }
7382
7383 lastQ.edgeCount = edgeCount;
7384 lastQ.compoundCount = compoundCount;
7385 selector.edgeCount = 0;
7386 selector.compoundCount = 0; // go on to next query
7387
7388 var nextQuery = selector[selector.length++] = newQuery();
7389 return nextQuery; // this is the new query to be filled by the following exprs
7390 }
7391}, {
7392 name: 'directedEdge',
7393 separator: true,
7394 regex: tokens.directedEdge,
7395 populate: function populate(selector, query) {
7396 if (selector.currentSubject == null) {
7397 // undirected edge
7398 var edgeQuery = newQuery();
7399 var source = query;
7400 var target = newQuery();
7401 edgeQuery.checks.push({
7402 type: Type.DIRECTED_EDGE,
7403 source: source,
7404 target: target
7405 }); // the query in the selector should be the edge rather than the source
7406
7407 replaceLastQuery(selector, query, edgeQuery);
7408 selector.edgeCount++; // we're now populating the target query with expressions that follow
7409
7410 return target;
7411 } else {
7412 // source/target
7413 var srcTgtQ = newQuery();
7414 var _source = query;
7415
7416 var _target = newQuery();
7417
7418 srcTgtQ.checks.push({
7419 type: Type.NODE_SOURCE,
7420 source: _source,
7421 target: _target
7422 }); // the query in the selector should be the neighbourhood rather than the node
7423
7424 replaceLastQuery(selector, query, srcTgtQ);
7425 selector.edgeCount++;
7426 return _target; // now populating the target with the following expressions
7427 }
7428 }
7429}, {
7430 name: 'undirectedEdge',
7431 separator: true,
7432 regex: tokens.undirectedEdge,
7433 populate: function populate(selector, query) {
7434 if (selector.currentSubject == null) {
7435 // undirected edge
7436 var edgeQuery = newQuery();
7437 var source = query;
7438 var target = newQuery();
7439 edgeQuery.checks.push({
7440 type: Type.UNDIRECTED_EDGE,
7441 nodes: [source, target]
7442 }); // the query in the selector should be the edge rather than the source
7443
7444 replaceLastQuery(selector, query, edgeQuery);
7445 selector.edgeCount++; // we're now populating the target query with expressions that follow
7446
7447 return target;
7448 } else {
7449 // neighbourhood
7450 var nhoodQ = newQuery();
7451 var node = query;
7452 var neighbor = newQuery();
7453 nhoodQ.checks.push({
7454 type: Type.NODE_NEIGHBOR,
7455 node: node,
7456 neighbor: neighbor
7457 }); // the query in the selector should be the neighbourhood rather than the node
7458
7459 replaceLastQuery(selector, query, nhoodQ);
7460 return neighbor; // now populating the neighbor with following expressions
7461 }
7462 }
7463}, {
7464 name: 'child',
7465 separator: true,
7466 regex: tokens.child,
7467 populate: function populate(selector, query) {
7468 if (selector.currentSubject == null) {
7469 // default: child query
7470 var parentChildQuery = newQuery();
7471 var child = newQuery();
7472 var parent = selector[selector.length - 1];
7473 parentChildQuery.checks.push({
7474 type: Type.CHILD,
7475 parent: parent,
7476 child: child
7477 }); // the query in the selector should be the '>' itself
7478
7479 replaceLastQuery(selector, query, parentChildQuery);
7480 selector.compoundCount++; // we're now populating the child query with expressions that follow
7481
7482 return child;
7483 } else if (selector.currentSubject === query) {
7484 // compound split query
7485 var compound = newQuery();
7486 var left = selector[selector.length - 1];
7487 var right = newQuery();
7488 var subject = newQuery();
7489
7490 var _child = newQuery();
7491
7492 var _parent = newQuery(); // set up the root compound q
7493
7494
7495 compound.checks.push({
7496 type: Type.COMPOUND_SPLIT,
7497 left: left,
7498 right: right,
7499 subject: subject
7500 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7501
7502 subject.checks = query.checks; // take the checks from the left
7503
7504 query.checks = [{
7505 type: Type.TRUE
7506 }]; // checks under left refs the subject implicitly
7507 // set up the right q
7508
7509 _parent.checks.push({
7510 type: Type.TRUE
7511 }); // parent implicitly refs the subject
7512
7513
7514 right.checks.push({
7515 type: Type.PARENT,
7516 // type is swapped on right side queries
7517 parent: _parent,
7518 child: _child // empty for now
7519
7520 });
7521 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7522
7523 selector.currentSubject = subject;
7524 selector.compoundCount++;
7525 return _child; // now populating the right side's child
7526 } else {
7527 // parent query
7528 // info for parent query
7529 var _parent2 = newQuery();
7530
7531 var _child2 = newQuery();
7532
7533 var pcQChecks = [{
7534 type: Type.PARENT,
7535 parent: _parent2,
7536 child: _child2
7537 }]; // the parent-child query takes the place of the query previously being populated
7538
7539 _parent2.checks = query.checks; // the previous query contains the checks for the parent
7540
7541 query.checks = pcQChecks; // pc query takes over
7542
7543 selector.compoundCount++;
7544 return _child2; // we're now populating the child
7545 }
7546 }
7547}, {
7548 name: 'descendant',
7549 separator: true,
7550 regex: tokens.descendant,
7551 populate: function populate(selector, query) {
7552 if (selector.currentSubject == null) {
7553 // default: descendant query
7554 var ancChQuery = newQuery();
7555 var descendant = newQuery();
7556 var ancestor = selector[selector.length - 1];
7557 ancChQuery.checks.push({
7558 type: Type.DESCENDANT,
7559 ancestor: ancestor,
7560 descendant: descendant
7561 }); // the query in the selector should be the '>' itself
7562
7563 replaceLastQuery(selector, query, ancChQuery);
7564 selector.compoundCount++; // we're now populating the descendant query with expressions that follow
7565
7566 return descendant;
7567 } else if (selector.currentSubject === query) {
7568 // compound split query
7569 var compound = newQuery();
7570 var left = selector[selector.length - 1];
7571 var right = newQuery();
7572 var subject = newQuery();
7573
7574 var _descendant = newQuery();
7575
7576 var _ancestor = newQuery(); // set up the root compound q
7577
7578
7579 compound.checks.push({
7580 type: Type.COMPOUND_SPLIT,
7581 left: left,
7582 right: right,
7583 subject: subject
7584 }); // populate the subject and replace the q at the old spot (within left) with TRUE
7585
7586 subject.checks = query.checks; // take the checks from the left
7587
7588 query.checks = [{
7589 type: Type.TRUE
7590 }]; // checks under left refs the subject implicitly
7591 // set up the right q
7592
7593 _ancestor.checks.push({
7594 type: Type.TRUE
7595 }); // ancestor implicitly refs the subject
7596
7597
7598 right.checks.push({
7599 type: Type.ANCESTOR,
7600 // type is swapped on right side queries
7601 ancestor: _ancestor,
7602 descendant: _descendant // empty for now
7603
7604 });
7605 replaceLastQuery(selector, left, compound); // update the ref since we moved things around for `query`
7606
7607 selector.currentSubject = subject;
7608 selector.compoundCount++;
7609 return _descendant; // now populating the right side's descendant
7610 } else {
7611 // ancestor query
7612 // info for parent query
7613 var _ancestor2 = newQuery();
7614
7615 var _descendant2 = newQuery();
7616
7617 var adQChecks = [{
7618 type: Type.ANCESTOR,
7619 ancestor: _ancestor2,
7620 descendant: _descendant2
7621 }]; // the parent-child query takes the place of the query previously being populated
7622
7623 _ancestor2.checks = query.checks; // the previous query contains the checks for the parent
7624
7625 query.checks = adQChecks; // pc query takes over
7626
7627 selector.compoundCount++;
7628 return _descendant2; // we're now populating the child
7629 }
7630 }
7631}, {
7632 name: 'subject',
7633 modifier: true,
7634 regex: tokens.subject,
7635 populate: function populate(selector, query) {
7636 if (selector.currentSubject != null && selector.currentSubject !== query) {
7637 warn('Redefinition of subject in selector `' + selector.toString() + '`');
7638 return false;
7639 }
7640
7641 selector.currentSubject = query;
7642 var topQ = selector[selector.length - 1];
7643 var topChk = topQ.checks[0];
7644 var topType = topChk == null ? null : topChk.type;
7645
7646 if (topType === Type.DIRECTED_EDGE) {
7647 // directed edge with subject on the target
7648 // change to target node check
7649 topChk.type = Type.NODE_TARGET;
7650 } else if (topType === Type.UNDIRECTED_EDGE) {
7651 // undirected edge with subject on the second node
7652 // change to neighbor check
7653 topChk.type = Type.NODE_NEIGHBOR;
7654 topChk.node = topChk.nodes[1]; // second node is subject
7655
7656 topChk.neighbor = topChk.nodes[0]; // clean up unused fields for new type
7657
7658 topChk.nodes = null;
7659 }
7660 }
7661}];
7662exprs.forEach(function (e) {
7663 return e.regexObj = new RegExp('^' + e.regex);
7664});
7665
7666/**
7667 * Of all the expressions, find the first match in the remaining text.
7668 * @param {string} remaining The remaining text to parse
7669 * @returns The matched expression and the newly remaining text `{ expr, match, name, remaining }`
7670 */
7671
7672var consumeExpr = function consumeExpr(remaining) {
7673 var expr;
7674 var match;
7675 var name;
7676
7677 for (var j = 0; j < exprs.length; j++) {
7678 var e = exprs[j];
7679 var n = e.name;
7680 var m = remaining.match(e.regexObj);
7681
7682 if (m != null) {
7683 match = m;
7684 expr = e;
7685 name = n;
7686 var consumed = m[0];
7687 remaining = remaining.substring(consumed.length);
7688 break; // we've consumed one expr, so we can return now
7689 }
7690 }
7691
7692 return {
7693 expr: expr,
7694 match: match,
7695 name: name,
7696 remaining: remaining
7697 };
7698};
7699/**
7700 * Consume all the leading whitespace
7701 * @param {string} remaining The text to consume
7702 * @returns The text with the leading whitespace removed
7703 */
7704
7705
7706var consumeWhitespace = function consumeWhitespace(remaining) {
7707 var match = remaining.match(/^\s+/);
7708
7709 if (match) {
7710 var consumed = match[0];
7711 remaining = remaining.substring(consumed.length);
7712 }
7713
7714 return remaining;
7715};
7716/**
7717 * Parse the string and store the parsed representation in the Selector.
7718 * @param {string} selector The selector string
7719 * @returns `true` if the selector was successfully parsed, `false` otherwise
7720 */
7721
7722
7723var parse = function parse(selector) {
7724 var self = this;
7725 var remaining = self.inputText = selector;
7726 var currentQuery = self[0] = newQuery();
7727 self.length = 1;
7728 remaining = consumeWhitespace(remaining); // get rid of leading whitespace
7729
7730 for (;;) {
7731 var exprInfo = consumeExpr(remaining);
7732
7733 if (exprInfo.expr == null) {
7734 warn('The selector `' + selector + '`is invalid');
7735 return false;
7736 } else {
7737 var args = exprInfo.match.slice(1); // let the token populate the selector object in currentQuery
7738
7739 var ret = exprInfo.expr.populate(self, currentQuery, args);
7740
7741 if (ret === false) {
7742 return false; // exit if population failed
7743 } else if (ret != null) {
7744 currentQuery = ret; // change the current query to be filled if the expr specifies
7745 }
7746 }
7747
7748 remaining = exprInfo.remaining; // we're done when there's nothing left to parse
7749
7750 if (remaining.match(/^\s*$/)) {
7751 break;
7752 }
7753 }
7754
7755 var lastQ = self[self.length - 1];
7756
7757 if (self.currentSubject != null) {
7758 lastQ.subject = self.currentSubject;
7759 }
7760
7761 lastQ.edgeCount = self.edgeCount;
7762 lastQ.compoundCount = self.compoundCount;
7763
7764 for (var i = 0; i < self.length; i++) {
7765 var q = self[i]; // in future, this could potentially be allowed if there were operator precedence and detection of invalid combinations
7766
7767 if (q.compoundCount > 0 && q.edgeCount > 0) {
7768 warn('The selector `' + selector + '` is invalid because it uses both a compound selector and an edge selector');
7769 return false;
7770 }
7771
7772 if (q.edgeCount > 1) {
7773 warn('The selector `' + selector + '` is invalid because it uses multiple edge selectors');
7774 return false;
7775 } else if (q.edgeCount === 1) {
7776 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.');
7777 }
7778 }
7779
7780 return true; // success
7781};
7782/**
7783 * Get the selector represented as a string. This value uses default formatting,
7784 * so things like spacing may differ from the input text passed to the constructor.
7785 * @returns {string} The selector string
7786 */
7787
7788
7789var toString = function toString() {
7790 if (this.toStringCache != null) {
7791 return this.toStringCache;
7792 }
7793
7794 var clean = function clean(obj) {
7795 if (obj == null) {
7796 return '';
7797 } else {
7798 return obj;
7799 }
7800 };
7801
7802 var cleanVal = function cleanVal(val) {
7803 if (string(val)) {
7804 return '"' + val + '"';
7805 } else {
7806 return clean(val);
7807 }
7808 };
7809
7810 var space = function space(val) {
7811 return ' ' + val + ' ';
7812 };
7813
7814 var checkToString = function checkToString(check, subject) {
7815 var type = check.type,
7816 value = check.value;
7817
7818 switch (type) {
7819 case Type.GROUP:
7820 {
7821 var group = clean(value);
7822 return group.substring(0, group.length - 1);
7823 }
7824
7825 case Type.DATA_COMPARE:
7826 {
7827 var field = check.field,
7828 operator = check.operator;
7829 return '[' + field + space(clean(operator)) + cleanVal(value) + ']';
7830 }
7831
7832 case Type.DATA_BOOL:
7833 {
7834 var _operator = check.operator,
7835 _field = check.field;
7836 return '[' + clean(_operator) + _field + ']';
7837 }
7838
7839 case Type.DATA_EXIST:
7840 {
7841 var _field2 = check.field;
7842 return '[' + _field2 + ']';
7843 }
7844
7845 case Type.META_COMPARE:
7846 {
7847 var _operator2 = check.operator,
7848 _field3 = check.field;
7849 return '[[' + _field3 + space(clean(_operator2)) + cleanVal(value) + ']]';
7850 }
7851
7852 case Type.STATE:
7853 {
7854 return value;
7855 }
7856
7857 case Type.ID:
7858 {
7859 return '#' + value;
7860 }
7861
7862 case Type.CLASS:
7863 {
7864 return '.' + value;
7865 }
7866
7867 case Type.PARENT:
7868 case Type.CHILD:
7869 {
7870 return queryToString(check.parent, subject) + space('>') + queryToString(check.child, subject);
7871 }
7872
7873 case Type.ANCESTOR:
7874 case Type.DESCENDANT:
7875 {
7876 return queryToString(check.ancestor, subject) + ' ' + queryToString(check.descendant, subject);
7877 }
7878
7879 case Type.COMPOUND_SPLIT:
7880 {
7881 var lhs = queryToString(check.left, subject);
7882 var sub = queryToString(check.subject, subject);
7883 var rhs = queryToString(check.right, subject);
7884 return lhs + (lhs.length > 0 ? ' ' : '') + sub + rhs;
7885 }
7886
7887 case Type.TRUE:
7888 {
7889 return '';
7890 }
7891 }
7892 };
7893
7894 var queryToString = function queryToString(query, subject) {
7895 return query.checks.reduce(function (str, chk, i) {
7896 return str + (subject === query && i === 0 ? '$' : '') + checkToString(chk, subject);
7897 }, '');
7898 };
7899
7900 var str = '';
7901
7902 for (var i = 0; i < this.length; i++) {
7903 var query = this[i];
7904 str += queryToString(query, query.subject);
7905
7906 if (this.length > 1 && i < this.length - 1) {
7907 str += ', ';
7908 }
7909 }
7910
7911 this.toStringCache = str;
7912 return str;
7913};
7914var parse$1 = {
7915 parse: parse,
7916 toString: toString
7917};
7918
7919var valCmp = function valCmp(fieldVal, operator, value) {
7920 var matches;
7921 var isFieldStr = string(fieldVal);
7922 var isFieldNum = number(fieldVal);
7923 var isValStr = string(value);
7924 var fieldStr, valStr;
7925 var caseInsensitive = false;
7926 var notExpr = false;
7927 var isIneqCmp = false;
7928
7929 if (operator.indexOf('!') >= 0) {
7930 operator = operator.replace('!', '');
7931 notExpr = true;
7932 }
7933
7934 if (operator.indexOf('@') >= 0) {
7935 operator = operator.replace('@', '');
7936 caseInsensitive = true;
7937 }
7938
7939 if (isFieldStr || isValStr || caseInsensitive) {
7940 fieldStr = !isFieldStr && !isFieldNum ? '' : '' + fieldVal;
7941 valStr = '' + value;
7942 } // if we're doing a case insensitive comparison, then we're using a STRING comparison
7943 // even if we're comparing numbers
7944
7945
7946 if (caseInsensitive) {
7947 fieldVal = fieldStr = fieldStr.toLowerCase();
7948 value = valStr = valStr.toLowerCase();
7949 }
7950
7951 switch (operator) {
7952 case '*=':
7953 matches = fieldStr.indexOf(valStr) >= 0;
7954 break;
7955
7956 case '$=':
7957 matches = fieldStr.indexOf(valStr, fieldStr.length - valStr.length) >= 0;
7958 break;
7959
7960 case '^=':
7961 matches = fieldStr.indexOf(valStr) === 0;
7962 break;
7963
7964 case '=':
7965 matches = fieldVal === value;
7966 break;
7967
7968 case '>':
7969 isIneqCmp = true;
7970 matches = fieldVal > value;
7971 break;
7972
7973 case '>=':
7974 isIneqCmp = true;
7975 matches = fieldVal >= value;
7976 break;
7977
7978 case '<':
7979 isIneqCmp = true;
7980 matches = fieldVal < value;
7981 break;
7982
7983 case '<=':
7984 isIneqCmp = true;
7985 matches = fieldVal <= value;
7986 break;
7987
7988 default:
7989 matches = false;
7990 break;
7991 } // apply the not op, but null vals for inequalities should always stay non-matching
7992
7993
7994 if (notExpr && (fieldVal != null || !isIneqCmp)) {
7995 matches = !matches;
7996 }
7997
7998 return matches;
7999};
8000var boolCmp = function boolCmp(fieldVal, operator) {
8001 switch (operator) {
8002 case '?':
8003 return fieldVal ? true : false;
8004
8005 case '!':
8006 return fieldVal ? false : true;
8007
8008 case '^':
8009 return fieldVal === undefined;
8010 }
8011};
8012var existCmp = function existCmp(fieldVal) {
8013 return fieldVal !== undefined;
8014};
8015var data = function data(ele, field) {
8016 return ele.data(field);
8017};
8018var meta = function meta(ele, field) {
8019 return ele[field]();
8020};
8021
8022/** A lookup of `match(check, ele)` functions by `Type` int */
8023
8024var match = [];
8025/**
8026 * Returns whether the query matches for the element
8027 * @param query The `{ type, value, ... }` query object
8028 * @param ele The element to compare against
8029*/
8030
8031var matches = function matches(query, ele) {
8032 return query.checks.every(function (chk) {
8033 return match[chk.type](chk, ele);
8034 });
8035};
8036
8037match[Type.GROUP] = function (check, ele) {
8038 var group = check.value;
8039 return group === '*' || group === ele.group();
8040};
8041
8042match[Type.STATE] = function (check, ele) {
8043 var stateSelector = check.value;
8044 return stateSelectorMatches(stateSelector, ele);
8045};
8046
8047match[Type.ID] = function (check, ele) {
8048 var id = check.value;
8049 return ele.id() === id;
8050};
8051
8052match[Type.CLASS] = function (check, ele) {
8053 var cls = check.value;
8054 return ele.hasClass(cls);
8055};
8056
8057match[Type.META_COMPARE] = function (check, ele) {
8058 var field = check.field,
8059 operator = check.operator,
8060 value = check.value;
8061 return valCmp(meta(ele, field), operator, value);
8062};
8063
8064match[Type.DATA_COMPARE] = function (check, ele) {
8065 var field = check.field,
8066 operator = check.operator,
8067 value = check.value;
8068 return valCmp(data(ele, field), operator, value);
8069};
8070
8071match[Type.DATA_BOOL] = function (check, ele) {
8072 var field = check.field,
8073 operator = check.operator;
8074 return boolCmp(data(ele, field), operator);
8075};
8076
8077match[Type.DATA_EXIST] = function (check, ele) {
8078 var field = check.field,
8079 operator = check.operator;
8080 return existCmp(data(ele, field));
8081};
8082
8083match[Type.UNDIRECTED_EDGE] = function (check, ele) {
8084 var qA = check.nodes[0];
8085 var qB = check.nodes[1];
8086 var src = ele.source();
8087 var tgt = ele.target();
8088 return matches(qA, src) && matches(qB, tgt) || matches(qB, src) && matches(qA, tgt);
8089};
8090
8091match[Type.NODE_NEIGHBOR] = function (check, ele) {
8092 return matches(check.node, ele) && ele.neighborhood().some(function (n) {
8093 return n.isNode() && matches(check.neighbor, n);
8094 });
8095};
8096
8097match[Type.DIRECTED_EDGE] = function (check, ele) {
8098 return matches(check.source, ele.source()) && matches(check.target, ele.target());
8099};
8100
8101match[Type.NODE_SOURCE] = function (check, ele) {
8102 return matches(check.source, ele) && ele.outgoers().some(function (n) {
8103 return n.isNode() && matches(check.target, n);
8104 });
8105};
8106
8107match[Type.NODE_TARGET] = function (check, ele) {
8108 return matches(check.target, ele) && ele.incomers().some(function (n) {
8109 return n.isNode() && matches(check.source, n);
8110 });
8111};
8112
8113match[Type.CHILD] = function (check, ele) {
8114 return matches(check.child, ele) && matches(check.parent, ele.parent());
8115};
8116
8117match[Type.PARENT] = function (check, ele) {
8118 return matches(check.parent, ele) && ele.children().some(function (c) {
8119 return matches(check.child, c);
8120 });
8121};
8122
8123match[Type.DESCENDANT] = function (check, ele) {
8124 return matches(check.descendant, ele) && ele.ancestors().some(function (a) {
8125 return matches(check.ancestor, a);
8126 });
8127};
8128
8129match[Type.ANCESTOR] = function (check, ele) {
8130 return matches(check.ancestor, ele) && ele.descendants().some(function (d) {
8131 return matches(check.descendant, d);
8132 });
8133};
8134
8135match[Type.COMPOUND_SPLIT] = function (check, ele) {
8136 return matches(check.subject, ele) && matches(check.left, ele) && matches(check.right, ele);
8137};
8138
8139match[Type.TRUE] = function () {
8140 return true;
8141};
8142
8143match[Type.COLLECTION] = function (check, ele) {
8144 var collection = check.value;
8145 return collection.has(ele);
8146};
8147
8148match[Type.FILTER] = function (check, ele) {
8149 var filter = check.value;
8150 return filter(ele);
8151};
8152
8153var filter = function filter(collection) {
8154 var self = this; // for 1 id #foo queries, just get the element
8155
8156 if (self.length === 1 && self[0].checks.length === 1 && self[0].checks[0].type === Type.ID) {
8157 return collection.getElementById(self[0].checks[0].value).collection();
8158 }
8159
8160 var selectorFunction = function selectorFunction(element) {
8161 for (var j = 0; j < self.length; j++) {
8162 var query = self[j];
8163
8164 if (matches(query, element)) {
8165 return true;
8166 }
8167 }
8168
8169 return false;
8170 };
8171
8172 if (self.text() == null) {
8173 selectorFunction = function selectorFunction() {
8174 return true;
8175 };
8176 }
8177
8178 return collection.filter(selectorFunction);
8179}; // filter
8180// does selector match a single element?
8181
8182
8183var matches$1 = function matches$1(ele) {
8184 var self = this;
8185
8186 for (var j = 0; j < self.length; j++) {
8187 var query = self[j];
8188
8189 if (matches(query, ele)) {
8190 return true;
8191 }
8192 }
8193
8194 return false;
8195}; // matches
8196
8197
8198var matching = {
8199 matches: matches$1,
8200 filter: filter
8201};
8202
8203var Selector = function Selector(selector) {
8204 this.inputText = selector;
8205 this.currentSubject = null;
8206 this.compoundCount = 0;
8207 this.edgeCount = 0;
8208 this.length = 0;
8209
8210 if (selector == null || string(selector) && selector.match(/^\s*$/)) ; else if (elementOrCollection(selector)) {
8211 this.addQuery({
8212 checks: [{
8213 type: Type.COLLECTION,
8214 value: selector.collection()
8215 }]
8216 });
8217 } else if (fn(selector)) {
8218 this.addQuery({
8219 checks: [{
8220 type: Type.FILTER,
8221 value: selector
8222 }]
8223 });
8224 } else if (string(selector)) {
8225 if (!this.parse(selector)) {
8226 this.invalid = true;
8227 }
8228 } else {
8229 error('A selector must be created from a string; found ');
8230 }
8231};
8232
8233var selfn = Selector.prototype;
8234[parse$1, matching].forEach(function (p) {
8235 return extend(selfn, p);
8236});
8237
8238selfn.text = function () {
8239 return this.inputText;
8240};
8241
8242selfn.size = function () {
8243 return this.length;
8244};
8245
8246selfn.eq = function (i) {
8247 return this[i];
8248};
8249
8250selfn.sameText = function (otherSel) {
8251 return !this.invalid && !otherSel.invalid && this.text() === otherSel.text();
8252};
8253
8254selfn.addQuery = function (q) {
8255 this[this.length++] = q;
8256};
8257
8258selfn.selector = selfn.toString;
8259
8260var elesfn$f = {
8261 allAre: function allAre(selector) {
8262 var selObj = new Selector(selector);
8263 return this.every(function (ele) {
8264 return selObj.matches(ele);
8265 });
8266 },
8267 is: function is(selector) {
8268 var selObj = new Selector(selector);
8269 return this.some(function (ele) {
8270 return selObj.matches(ele);
8271 });
8272 },
8273 some: function some(fn, thisArg) {
8274 for (var i = 0; i < this.length; i++) {
8275 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8276
8277 if (ret) {
8278 return true;
8279 }
8280 }
8281
8282 return false;
8283 },
8284 every: function every(fn, thisArg) {
8285 for (var i = 0; i < this.length; i++) {
8286 var ret = !thisArg ? fn(this[i], i, this) : fn.apply(thisArg, [this[i], i, this]);
8287
8288 if (!ret) {
8289 return false;
8290 }
8291 }
8292
8293 return true;
8294 },
8295 same: function same(collection) {
8296 // cheap collection ref check
8297 if (this === collection) {
8298 return true;
8299 }
8300
8301 collection = this.cy().collection(collection);
8302 var thisLength = this.length;
8303 var collectionLength = collection.length; // cheap length check
8304
8305 if (thisLength !== collectionLength) {
8306 return false;
8307 } // cheap element ref check
8308
8309
8310 if (thisLength === 1) {
8311 return this[0] === collection[0];
8312 }
8313
8314 return this.every(function (ele) {
8315 return collection.hasElementWithId(ele.id());
8316 });
8317 },
8318 anySame: function anySame(collection) {
8319 collection = this.cy().collection(collection);
8320 return this.some(function (ele) {
8321 return collection.hasElementWithId(ele.id());
8322 });
8323 },
8324 allAreNeighbors: function allAreNeighbors(collection) {
8325 collection = this.cy().collection(collection);
8326 var nhood = this.neighborhood();
8327 return collection.every(function (ele) {
8328 return nhood.hasElementWithId(ele.id());
8329 });
8330 },
8331 contains: function contains(collection) {
8332 collection = this.cy().collection(collection);
8333 var self = this;
8334 return collection.every(function (ele) {
8335 return self.hasElementWithId(ele.id());
8336 });
8337 }
8338};
8339elesfn$f.allAreNeighbours = elesfn$f.allAreNeighbors;
8340elesfn$f.has = elesfn$f.contains;
8341elesfn$f.equal = elesfn$f.equals = elesfn$f.same;
8342
8343var cache = function cache(fn, name) {
8344 return function traversalCache(arg1, arg2, arg3, arg4) {
8345 var selectorOrEles = arg1;
8346 var eles = this;
8347 var key;
8348
8349 if (selectorOrEles == null) {
8350 key = '';
8351 } else if (elementOrCollection(selectorOrEles) && selectorOrEles.length === 1) {
8352 key = selectorOrEles.id();
8353 }
8354
8355 if (eles.length === 1 && key) {
8356 var _p = eles[0]._private;
8357 var tch = _p.traversalCache = _p.traversalCache || {};
8358 var ch = tch[name] = tch[name] || [];
8359 var hash = hashString(key);
8360 var cacheHit = ch[hash];
8361
8362 if (cacheHit) {
8363 return cacheHit;
8364 } else {
8365 return ch[hash] = fn.call(eles, arg1, arg2, arg3, arg4);
8366 }
8367 } else {
8368 return fn.call(eles, arg1, arg2, arg3, arg4);
8369 }
8370 };
8371};
8372
8373var elesfn$g = {
8374 parent: function parent(selector) {
8375 var parents = []; // optimisation for single ele call
8376
8377 if (this.length === 1) {
8378 var parent = this[0]._private.parent;
8379
8380 if (parent) {
8381 return parent;
8382 }
8383 }
8384
8385 for (var i = 0; i < this.length; i++) {
8386 var ele = this[i];
8387 var _parent = ele._private.parent;
8388
8389 if (_parent) {
8390 parents.push(_parent);
8391 }
8392 }
8393
8394 return this.spawn(parents, true).filter(selector);
8395 },
8396 parents: function parents(selector) {
8397 var parents = [];
8398 var eles = this.parent();
8399
8400 while (eles.nonempty()) {
8401 for (var i = 0; i < eles.length; i++) {
8402 var ele = eles[i];
8403 parents.push(ele);
8404 }
8405
8406 eles = eles.parent();
8407 }
8408
8409 return this.spawn(parents, true).filter(selector);
8410 },
8411 commonAncestors: function commonAncestors(selector) {
8412 var ancestors;
8413
8414 for (var i = 0; i < this.length; i++) {
8415 var ele = this[i];
8416 var parents = ele.parents();
8417 ancestors = ancestors || parents;
8418 ancestors = ancestors.intersect(parents); // current list must be common with current ele parents set
8419 }
8420
8421 return ancestors.filter(selector);
8422 },
8423 orphans: function orphans(selector) {
8424 return this.stdFilter(function (ele) {
8425 return ele.isOrphan();
8426 }).filter(selector);
8427 },
8428 nonorphans: function nonorphans(selector) {
8429 return this.stdFilter(function (ele) {
8430 return ele.isChild();
8431 }).filter(selector);
8432 },
8433 children: cache(function (selector) {
8434 var children = [];
8435
8436 for (var i = 0; i < this.length; i++) {
8437 var ele = this[i];
8438 var eleChildren = ele._private.children;
8439
8440 for (var j = 0; j < eleChildren.length; j++) {
8441 children.push(eleChildren[j]);
8442 }
8443 }
8444
8445 return this.spawn(children, true).filter(selector);
8446 }, 'children'),
8447 siblings: function siblings(selector) {
8448 return this.parent().children().not(this).filter(selector);
8449 },
8450 isParent: function isParent() {
8451 var ele = this[0];
8452
8453 if (ele) {
8454 return ele.isNode() && ele._private.children.length !== 0;
8455 }
8456 },
8457 isChildless: function isChildless() {
8458 var ele = this[0];
8459
8460 if (ele) {
8461 return ele.isNode() && ele._private.children.length === 0;
8462 }
8463 },
8464 isChild: function isChild() {
8465 var ele = this[0];
8466
8467 if (ele) {
8468 return ele.isNode() && ele._private.parent != null;
8469 }
8470 },
8471 isOrphan: function isOrphan() {
8472 var ele = this[0];
8473
8474 if (ele) {
8475 return ele.isNode() && ele._private.parent == null;
8476 }
8477 },
8478 descendants: function descendants(selector) {
8479 var elements = [];
8480
8481 function add(eles) {
8482 for (var i = 0; i < eles.length; i++) {
8483 var ele = eles[i];
8484 elements.push(ele);
8485
8486 if (ele.children().nonempty()) {
8487 add(ele.children());
8488 }
8489 }
8490 }
8491
8492 add(this.children());
8493 return this.spawn(elements, true).filter(selector);
8494 }
8495};
8496
8497function forEachCompound(eles, fn, includeSelf, recursiveStep) {
8498 var q = [];
8499 var did = new Set$1();
8500 var cy = eles.cy();
8501 var hasCompounds = cy.hasCompoundNodes();
8502
8503 for (var i = 0; i < eles.length; i++) {
8504 var ele = eles[i];
8505
8506 if (includeSelf) {
8507 q.push(ele);
8508 } else if (hasCompounds) {
8509 recursiveStep(q, did, ele);
8510 }
8511 }
8512
8513 while (q.length > 0) {
8514 var _ele = q.shift();
8515
8516 fn(_ele);
8517 did.add(_ele.id());
8518
8519 if (hasCompounds) {
8520 recursiveStep(q, did, _ele);
8521 }
8522 }
8523
8524 return eles;
8525}
8526
8527function addChildren(q, did, ele) {
8528 if (ele.isParent()) {
8529 var children = ele._private.children;
8530
8531 for (var i = 0; i < children.length; i++) {
8532 var child = children[i];
8533
8534 if (!did.has(child.id())) {
8535 q.push(child);
8536 }
8537 }
8538 }
8539} // very efficient version of eles.add( eles.descendants() ).forEach()
8540// for internal use
8541
8542
8543elesfn$g.forEachDown = function (fn) {
8544 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8545 return forEachCompound(this, fn, includeSelf, addChildren);
8546};
8547
8548function addParent(q, did, ele) {
8549 if (ele.isChild()) {
8550 var parent = ele._private.parent;
8551
8552 if (!did.has(parent.id())) {
8553 q.push(parent);
8554 }
8555 }
8556}
8557
8558elesfn$g.forEachUp = function (fn) {
8559 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8560 return forEachCompound(this, fn, includeSelf, addParent);
8561};
8562
8563function addParentAndChildren(q, did, ele) {
8564 addParent(q, did, ele);
8565 addChildren(q, did, ele);
8566}
8567
8568elesfn$g.forEachUpAndDown = function (fn) {
8569 var includeSelf = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
8570 return forEachCompound(this, fn, includeSelf, addParentAndChildren);
8571}; // aliases
8572
8573
8574elesfn$g.ancestors = elesfn$g.parents;
8575
8576var fn$1, elesfn$h;
8577fn$1 = elesfn$h = {
8578 data: define$3.data({
8579 field: 'data',
8580 bindingEvent: 'data',
8581 allowBinding: true,
8582 allowSetting: true,
8583 settingEvent: 'data',
8584 settingTriggersEvent: true,
8585 triggerFnName: 'trigger',
8586 allowGetting: true,
8587 immutableKeys: {
8588 'id': true,
8589 'source': true,
8590 'target': true,
8591 'parent': true
8592 },
8593 updateStyle: true
8594 }),
8595 removeData: define$3.removeData({
8596 field: 'data',
8597 event: 'data',
8598 triggerFnName: 'trigger',
8599 triggerEvent: true,
8600 immutableKeys: {
8601 'id': true,
8602 'source': true,
8603 'target': true,
8604 'parent': true
8605 },
8606 updateStyle: true
8607 }),
8608 scratch: define$3.data({
8609 field: 'scratch',
8610 bindingEvent: 'scratch',
8611 allowBinding: true,
8612 allowSetting: true,
8613 settingEvent: 'scratch',
8614 settingTriggersEvent: true,
8615 triggerFnName: 'trigger',
8616 allowGetting: true,
8617 updateStyle: true
8618 }),
8619 removeScratch: define$3.removeData({
8620 field: 'scratch',
8621 event: 'scratch',
8622 triggerFnName: 'trigger',
8623 triggerEvent: true,
8624 updateStyle: true
8625 }),
8626 rscratch: define$3.data({
8627 field: 'rscratch',
8628 allowBinding: false,
8629 allowSetting: true,
8630 settingTriggersEvent: false,
8631 allowGetting: true
8632 }),
8633 removeRscratch: define$3.removeData({
8634 field: 'rscratch',
8635 triggerEvent: false
8636 }),
8637 id: function id() {
8638 var ele = this[0];
8639
8640 if (ele) {
8641 return ele._private.data.id;
8642 }
8643 }
8644}; // aliases
8645
8646fn$1.attr = fn$1.data;
8647fn$1.removeAttr = fn$1.removeData;
8648var data$1 = elesfn$h;
8649
8650var elesfn$i = {};
8651
8652function defineDegreeFunction(callback) {
8653 return function (includeLoops) {
8654 var self = this;
8655
8656 if (includeLoops === undefined) {
8657 includeLoops = true;
8658 }
8659
8660 if (self.length === 0) {
8661 return;
8662 }
8663
8664 if (self.isNode() && !self.removed()) {
8665 var degree = 0;
8666 var node = self[0];
8667 var connectedEdges = node._private.edges;
8668
8669 for (var i = 0; i < connectedEdges.length; i++) {
8670 var edge = connectedEdges[i];
8671
8672 if (!includeLoops && edge.isLoop()) {
8673 continue;
8674 }
8675
8676 degree += callback(node, edge);
8677 }
8678
8679 return degree;
8680 } else {
8681 return;
8682 }
8683 };
8684}
8685
8686extend(elesfn$i, {
8687 degree: defineDegreeFunction(function (node, edge) {
8688 if (edge.source().same(edge.target())) {
8689 return 2;
8690 } else {
8691 return 1;
8692 }
8693 }),
8694 indegree: defineDegreeFunction(function (node, edge) {
8695 if (edge.target().same(node)) {
8696 return 1;
8697 } else {
8698 return 0;
8699 }
8700 }),
8701 outdegree: defineDegreeFunction(function (node, edge) {
8702 if (edge.source().same(node)) {
8703 return 1;
8704 } else {
8705 return 0;
8706 }
8707 })
8708});
8709
8710function defineDegreeBoundsFunction(degreeFn, callback) {
8711 return function (includeLoops) {
8712 var ret;
8713 var nodes = this.nodes();
8714
8715 for (var i = 0; i < nodes.length; i++) {
8716 var ele = nodes[i];
8717 var degree = ele[degreeFn](includeLoops);
8718
8719 if (degree !== undefined && (ret === undefined || callback(degree, ret))) {
8720 ret = degree;
8721 }
8722 }
8723
8724 return ret;
8725 };
8726}
8727
8728extend(elesfn$i, {
8729 minDegree: defineDegreeBoundsFunction('degree', function (degree, min) {
8730 return degree < min;
8731 }),
8732 maxDegree: defineDegreeBoundsFunction('degree', function (degree, max) {
8733 return degree > max;
8734 }),
8735 minIndegree: defineDegreeBoundsFunction('indegree', function (degree, min) {
8736 return degree < min;
8737 }),
8738 maxIndegree: defineDegreeBoundsFunction('indegree', function (degree, max) {
8739 return degree > max;
8740 }),
8741 minOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, min) {
8742 return degree < min;
8743 }),
8744 maxOutdegree: defineDegreeBoundsFunction('outdegree', function (degree, max) {
8745 return degree > max;
8746 })
8747});
8748extend(elesfn$i, {
8749 totalDegree: function totalDegree(includeLoops) {
8750 var total = 0;
8751 var nodes = this.nodes();
8752
8753 for (var i = 0; i < nodes.length; i++) {
8754 total += nodes[i].degree(includeLoops);
8755 }
8756
8757 return total;
8758 }
8759});
8760
8761var fn$2, elesfn$j;
8762
8763var beforePositionSet = function beforePositionSet(eles, newPos, silent) {
8764 for (var i = 0; i < eles.length; i++) {
8765 var ele = eles[i];
8766
8767 if (!ele.locked()) {
8768 var oldPos = ele._private.position;
8769 var delta = {
8770 x: newPos.x != null ? newPos.x - oldPos.x : 0,
8771 y: newPos.y != null ? newPos.y - oldPos.y : 0
8772 };
8773
8774 if (ele.isParent() && !(delta.x === 0 && delta.y === 0)) {
8775 ele.children().shift(delta, silent);
8776 }
8777
8778 ele.dirtyBoundingBoxCache();
8779 }
8780 }
8781};
8782
8783var positionDef = {
8784 field: 'position',
8785 bindingEvent: 'position',
8786 allowBinding: true,
8787 allowSetting: true,
8788 settingEvent: 'position',
8789 settingTriggersEvent: true,
8790 triggerFnName: 'emitAndNotify',
8791 allowGetting: true,
8792 validKeys: ['x', 'y'],
8793 beforeGet: function beforeGet(ele) {
8794 ele.updateCompoundBounds();
8795 },
8796 beforeSet: function beforeSet(eles, newPos) {
8797 beforePositionSet(eles, newPos, false);
8798 },
8799 onSet: function onSet(eles) {
8800 eles.dirtyCompoundBoundsCache();
8801 },
8802 canSet: function canSet(ele) {
8803 return !ele.locked();
8804 }
8805};
8806fn$2 = elesfn$j = {
8807 position: define$3.data(positionDef),
8808 // position but no notification to renderer
8809 silentPosition: define$3.data(extend({}, positionDef, {
8810 allowBinding: false,
8811 allowSetting: true,
8812 settingTriggersEvent: false,
8813 allowGetting: false,
8814 beforeSet: function beforeSet(eles, newPos) {
8815 beforePositionSet(eles, newPos, true);
8816 },
8817 onSet: function onSet(eles) {
8818 eles.dirtyCompoundBoundsCache();
8819 }
8820 })),
8821 positions: function positions(pos, silent) {
8822 if (plainObject(pos)) {
8823 if (silent) {
8824 this.silentPosition(pos);
8825 } else {
8826 this.position(pos);
8827 }
8828 } else if (fn(pos)) {
8829 var _fn = pos;
8830 var cy = this.cy();
8831 cy.startBatch();
8832
8833 for (var i = 0; i < this.length; i++) {
8834 var ele = this[i];
8835
8836 var _pos = void 0;
8837
8838 if (_pos = _fn(ele, i)) {
8839 if (silent) {
8840 ele.silentPosition(_pos);
8841 } else {
8842 ele.position(_pos);
8843 }
8844 }
8845 }
8846
8847 cy.endBatch();
8848 }
8849
8850 return this; // chaining
8851 },
8852 silentPositions: function silentPositions(pos) {
8853 return this.positions(pos, true);
8854 },
8855 shift: function shift(dim, val, silent) {
8856 var delta;
8857
8858 if (plainObject(dim)) {
8859 delta = {
8860 x: number(dim.x) ? dim.x : 0,
8861 y: number(dim.y) ? dim.y : 0
8862 };
8863 silent = val;
8864 } else if (string(dim) && number(val)) {
8865 delta = {
8866 x: 0,
8867 y: 0
8868 };
8869 delta[dim] = val;
8870 }
8871
8872 if (delta != null) {
8873 var cy = this.cy();
8874 cy.startBatch();
8875
8876 for (var i = 0; i < this.length; i++) {
8877 var ele = this[i];
8878 var pos = ele.position();
8879 var newPos = {
8880 x: pos.x + delta.x,
8881 y: pos.y + delta.y
8882 };
8883
8884 if (silent) {
8885 ele.silentPosition(newPos);
8886 } else {
8887 ele.position(newPos);
8888 }
8889 }
8890
8891 cy.endBatch();
8892 }
8893
8894 return this;
8895 },
8896 silentShift: function silentShift(dim, val) {
8897 if (plainObject(dim)) {
8898 this.shift(dim, true);
8899 } else if (string(dim) && number(val)) {
8900 this.shift(dim, val, true);
8901 }
8902
8903 return this;
8904 },
8905 // get/set the rendered (i.e. on screen) positon of the element
8906 renderedPosition: function renderedPosition(dim, val) {
8907 var ele = this[0];
8908 var cy = this.cy();
8909 var zoom = cy.zoom();
8910 var pan = cy.pan();
8911 var rpos = plainObject(dim) ? dim : undefined;
8912 var setting = rpos !== undefined || val !== undefined && string(dim);
8913
8914 if (ele && ele.isNode()) {
8915 // must have an element and must be a node to return position
8916 if (setting) {
8917 for (var i = 0; i < this.length; i++) {
8918 var _ele = this[i];
8919
8920 if (val !== undefined) {
8921 // set one dimension
8922 _ele.position(dim, (val - pan[dim]) / zoom);
8923 } else if (rpos !== undefined) {
8924 // set whole position
8925 _ele.position(renderedToModelPosition(rpos, zoom, pan));
8926 }
8927 }
8928 } else {
8929 // getting
8930 var pos = ele.position();
8931 rpos = modelToRenderedPosition(pos, zoom, pan);
8932
8933 if (dim === undefined) {
8934 // then return the whole rendered position
8935 return rpos;
8936 } else {
8937 // then return the specified dimension
8938 return rpos[dim];
8939 }
8940 }
8941 } else if (!setting) {
8942 return undefined; // for empty collection case
8943 }
8944
8945 return this; // chaining
8946 },
8947 // get/set the position relative to the parent
8948 relativePosition: function relativePosition(dim, val) {
8949 var ele = this[0];
8950 var cy = this.cy();
8951 var ppos = plainObject(dim) ? dim : undefined;
8952 var setting = ppos !== undefined || val !== undefined && string(dim);
8953 var hasCompoundNodes = cy.hasCompoundNodes();
8954
8955 if (ele && ele.isNode()) {
8956 // must have an element and must be a node to return position
8957 if (setting) {
8958 for (var i = 0; i < this.length; i++) {
8959 var _ele2 = this[i];
8960 var parent = hasCompoundNodes ? _ele2.parent() : null;
8961 var hasParent = parent && parent.length > 0;
8962 var relativeToParent = hasParent;
8963
8964 if (hasParent) {
8965 parent = parent[0];
8966 }
8967
8968 var origin = relativeToParent ? parent.position() : {
8969 x: 0,
8970 y: 0
8971 };
8972
8973 if (val !== undefined) {
8974 // set one dimension
8975 _ele2.position(dim, val + origin[dim]);
8976 } else if (ppos !== undefined) {
8977 // set whole position
8978 _ele2.position({
8979 x: ppos.x + origin.x,
8980 y: ppos.y + origin.y
8981 });
8982 }
8983 }
8984 } else {
8985 // getting
8986 var pos = ele.position();
8987
8988 var _parent = hasCompoundNodes ? ele.parent() : null;
8989
8990 var _hasParent = _parent && _parent.length > 0;
8991
8992 var _relativeToParent = _hasParent;
8993
8994 if (_hasParent) {
8995 _parent = _parent[0];
8996 }
8997
8998 var _origin = _relativeToParent ? _parent.position() : {
8999 x: 0,
9000 y: 0
9001 };
9002
9003 ppos = {
9004 x: pos.x - _origin.x,
9005 y: pos.y - _origin.y
9006 };
9007
9008 if (dim === undefined) {
9009 // then return the whole rendered position
9010 return ppos;
9011 } else {
9012 // then return the specified dimension
9013 return ppos[dim];
9014 }
9015 }
9016 } else if (!setting) {
9017 return undefined; // for empty collection case
9018 }
9019
9020 return this; // chaining
9021 }
9022}; // aliases
9023
9024fn$2.modelPosition = fn$2.point = fn$2.position;
9025fn$2.modelPositions = fn$2.points = fn$2.positions;
9026fn$2.renderedPoint = fn$2.renderedPosition;
9027fn$2.relativePoint = fn$2.relativePosition;
9028var position = elesfn$j;
9029
9030var fn$3, elesfn$k;
9031fn$3 = elesfn$k = {};
9032
9033elesfn$k.renderedBoundingBox = function (options) {
9034 var bb = this.boundingBox(options);
9035 var cy = this.cy();
9036 var zoom = cy.zoom();
9037 var pan = cy.pan();
9038 var x1 = bb.x1 * zoom + pan.x;
9039 var x2 = bb.x2 * zoom + pan.x;
9040 var y1 = bb.y1 * zoom + pan.y;
9041 var y2 = bb.y2 * zoom + pan.y;
9042 return {
9043 x1: x1,
9044 x2: x2,
9045 y1: y1,
9046 y2: y2,
9047 w: x2 - x1,
9048 h: y2 - y1
9049 };
9050};
9051
9052elesfn$k.dirtyCompoundBoundsCache = function () {
9053 var silent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9054 var cy = this.cy();
9055
9056 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9057 return this;
9058 }
9059
9060 this.forEachUp(function (ele) {
9061 if (ele.isParent()) {
9062 var _p = ele._private;
9063 _p.compoundBoundsClean = false;
9064 _p.bbCache = null;
9065
9066 if (!silent) {
9067 ele.emitAndNotify('bounds');
9068 }
9069 }
9070 });
9071 return this;
9072};
9073
9074elesfn$k.updateCompoundBounds = function () {
9075 var force = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
9076 var cy = this.cy(); // not possible to do on non-compound graphs or with the style disabled
9077
9078 if (!cy.styleEnabled() || !cy.hasCompoundNodes()) {
9079 return this;
9080 } // save cycles when batching -- but bounds will be stale (or not exist yet)
9081
9082
9083 if (!force && cy.batching()) {
9084 return this;
9085 }
9086
9087 function update(parent) {
9088 if (!parent.isParent()) {
9089 return;
9090 }
9091
9092 var _p = parent._private;
9093 var children = parent.children();
9094 var includeLabels = parent.pstyle('compound-sizing-wrt-labels').value === 'include';
9095 var min = {
9096 width: {
9097 val: parent.pstyle('min-width').pfValue,
9098 left: parent.pstyle('min-width-bias-left'),
9099 right: parent.pstyle('min-width-bias-right')
9100 },
9101 height: {
9102 val: parent.pstyle('min-height').pfValue,
9103 top: parent.pstyle('min-height-bias-top'),
9104 bottom: parent.pstyle('min-height-bias-bottom')
9105 }
9106 };
9107 var bb = children.boundingBox({
9108 includeLabels: includeLabels,
9109 includeOverlays: false,
9110 // updating the compound bounds happens outside of the regular
9111 // cache cycle (i.e. before fired events)
9112 useCache: false
9113 });
9114 var pos = _p.position; // if children take up zero area then keep position and fall back on stylesheet w/h
9115
9116 if (bb.w === 0 || bb.h === 0) {
9117 bb = {
9118 w: parent.pstyle('width').pfValue,
9119 h: parent.pstyle('height').pfValue
9120 };
9121 bb.x1 = pos.x - bb.w / 2;
9122 bb.x2 = pos.x + bb.w / 2;
9123 bb.y1 = pos.y - bb.h / 2;
9124 bb.y2 = pos.y + bb.h / 2;
9125 }
9126
9127 function computeBiasValues(propDiff, propBias, propBiasComplement) {
9128 var biasDiff = 0;
9129 var biasComplementDiff = 0;
9130 var biasTotal = propBias + propBiasComplement;
9131
9132 if (propDiff > 0 && biasTotal > 0) {
9133 biasDiff = propBias / biasTotal * propDiff;
9134 biasComplementDiff = propBiasComplement / biasTotal * propDiff;
9135 }
9136
9137 return {
9138 biasDiff: biasDiff,
9139 biasComplementDiff: biasComplementDiff
9140 };
9141 }
9142
9143 function computePaddingValues(width, height, paddingObject, relativeTo) {
9144 // Assuming percentage is number from 0 to 1
9145 if (paddingObject.units === '%') {
9146 switch (relativeTo) {
9147 case 'width':
9148 return width > 0 ? paddingObject.pfValue * width : 0;
9149
9150 case 'height':
9151 return height > 0 ? paddingObject.pfValue * height : 0;
9152
9153 case 'average':
9154 return width > 0 && height > 0 ? paddingObject.pfValue * (width + height) / 2 : 0;
9155
9156 case 'min':
9157 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * height : paddingObject.pfValue * width : 0;
9158
9159 case 'max':
9160 return width > 0 && height > 0 ? width > height ? paddingObject.pfValue * width : paddingObject.pfValue * height : 0;
9161
9162 default:
9163 return 0;
9164 }
9165 } else if (paddingObject.units === 'px') {
9166 return paddingObject.pfValue;
9167 } else {
9168 return 0;
9169 }
9170 }
9171
9172 var leftVal = min.width.left.value;
9173
9174 if (min.width.left.units === 'px' && min.width.val > 0) {
9175 leftVal = leftVal * 100 / min.width.val;
9176 }
9177
9178 var rightVal = min.width.right.value;
9179
9180 if (min.width.right.units === 'px' && min.width.val > 0) {
9181 rightVal = rightVal * 100 / min.width.val;
9182 }
9183
9184 var topVal = min.height.top.value;
9185
9186 if (min.height.top.units === 'px' && min.height.val > 0) {
9187 topVal = topVal * 100 / min.height.val;
9188 }
9189
9190 var bottomVal = min.height.bottom.value;
9191
9192 if (min.height.bottom.units === 'px' && min.height.val > 0) {
9193 bottomVal = bottomVal * 100 / min.height.val;
9194 }
9195
9196 var widthBiasDiffs = computeBiasValues(min.width.val - bb.w, leftVal, rightVal);
9197 var diffLeft = widthBiasDiffs.biasDiff;
9198 var diffRight = widthBiasDiffs.biasComplementDiff;
9199 var heightBiasDiffs = computeBiasValues(min.height.val - bb.h, topVal, bottomVal);
9200 var diffTop = heightBiasDiffs.biasDiff;
9201 var diffBottom = heightBiasDiffs.biasComplementDiff;
9202 _p.autoPadding = computePaddingValues(bb.w, bb.h, parent.pstyle('padding'), parent.pstyle('padding-relative-to').value);
9203 _p.autoWidth = Math.max(bb.w, min.width.val);
9204 pos.x = (-diffLeft + bb.x1 + bb.x2 + diffRight) / 2;
9205 _p.autoHeight = Math.max(bb.h, min.height.val);
9206 pos.y = (-diffTop + bb.y1 + bb.y2 + diffBottom) / 2;
9207 }
9208
9209 for (var i = 0; i < this.length; i++) {
9210 var ele = this[i];
9211 var _p = ele._private;
9212
9213 if (!_p.compoundBoundsClean || force) {
9214 update(ele);
9215
9216 if (!cy.batching()) {
9217 _p.compoundBoundsClean = true;
9218 }
9219 }
9220 }
9221
9222 return this;
9223};
9224
9225var noninf = function noninf(x) {
9226 if (x === Infinity || x === -Infinity) {
9227 return 0;
9228 }
9229
9230 return x;
9231};
9232
9233var updateBounds = function updateBounds(b, x1, y1, x2, y2) {
9234 // don't update with zero area boxes
9235 if (x2 - x1 === 0 || y2 - y1 === 0) {
9236 return;
9237 } // don't update with null dim
9238
9239
9240 if (x1 == null || y1 == null || x2 == null || y2 == null) {
9241 return;
9242 }
9243
9244 b.x1 = x1 < b.x1 ? x1 : b.x1;
9245 b.x2 = x2 > b.x2 ? x2 : b.x2;
9246 b.y1 = y1 < b.y1 ? y1 : b.y1;
9247 b.y2 = y2 > b.y2 ? y2 : b.y2;
9248 b.w = b.x2 - b.x1;
9249 b.h = b.y2 - b.y1;
9250};
9251
9252var updateBoundsFromBox = function updateBoundsFromBox(b, b2) {
9253 if (b2 == null) {
9254 return b;
9255 }
9256
9257 return updateBounds(b, b2.x1, b2.y1, b2.x2, b2.y2);
9258};
9259
9260var prefixedProperty = function prefixedProperty(obj, field, prefix) {
9261 return getPrefixedProperty(obj, field, prefix);
9262};
9263
9264var updateBoundsFromArrow = function updateBoundsFromArrow(bounds, ele, prefix) {
9265 if (ele.cy().headless()) {
9266 return;
9267 }
9268
9269 var _p = ele._private;
9270 var rstyle = _p.rstyle;
9271 var halfArW = rstyle.arrowWidth / 2;
9272 var arrowType = ele.pstyle(prefix + '-arrow-shape').value;
9273 var x;
9274 var y;
9275
9276 if (arrowType !== 'none') {
9277 if (prefix === 'source') {
9278 x = rstyle.srcX;
9279 y = rstyle.srcY;
9280 } else if (prefix === 'target') {
9281 x = rstyle.tgtX;
9282 y = rstyle.tgtY;
9283 } else {
9284 x = rstyle.midX;
9285 y = rstyle.midY;
9286 } // always store the individual arrow bounds
9287
9288
9289 var bbs = _p.arrowBounds = _p.arrowBounds || {};
9290 var bb = bbs[prefix] = bbs[prefix] || {};
9291 bb.x1 = x - halfArW;
9292 bb.y1 = y - halfArW;
9293 bb.x2 = x + halfArW;
9294 bb.y2 = y + halfArW;
9295 bb.w = bb.x2 - bb.x1;
9296 bb.h = bb.y2 - bb.y1;
9297 expandBoundingBox(bb, 1);
9298 updateBounds(bounds, bb.x1, bb.y1, bb.x2, bb.y2);
9299 }
9300};
9301
9302var updateBoundsFromLabel = function updateBoundsFromLabel(bounds, ele, prefix) {
9303 if (ele.cy().headless()) {
9304 return;
9305 }
9306
9307 var prefixDash;
9308
9309 if (prefix) {
9310 prefixDash = prefix + '-';
9311 } else {
9312 prefixDash = '';
9313 }
9314
9315 var _p = ele._private;
9316 var rstyle = _p.rstyle;
9317 var label = ele.pstyle(prefixDash + 'label').strValue;
9318
9319 if (label) {
9320 var halign = ele.pstyle('text-halign');
9321 var valign = ele.pstyle('text-valign');
9322 var labelWidth = prefixedProperty(rstyle, 'labelWidth', prefix);
9323 var labelHeight = prefixedProperty(rstyle, 'labelHeight', prefix);
9324 var labelX = prefixedProperty(rstyle, 'labelX', prefix);
9325 var labelY = prefixedProperty(rstyle, 'labelY', prefix);
9326 var marginX = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
9327 var marginY = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
9328 var isEdge = ele.isEdge();
9329 var rotation = ele.pstyle(prefixDash + 'text-rotation');
9330 var outlineWidth = ele.pstyle('text-outline-width').pfValue;
9331 var borderWidth = ele.pstyle('text-border-width').pfValue;
9332 var halfBorderWidth = borderWidth / 2;
9333 var padding = ele.pstyle('text-background-padding').pfValue;
9334 var marginOfError = 2; // expand to work around browser dimension inaccuracies
9335
9336 var lh = labelHeight;
9337 var lw = labelWidth;
9338 var lw_2 = lw / 2;
9339 var lh_2 = lh / 2;
9340 var lx1, lx2, ly1, ly2;
9341
9342 if (isEdge) {
9343 lx1 = labelX - lw_2;
9344 lx2 = labelX + lw_2;
9345 ly1 = labelY - lh_2;
9346 ly2 = labelY + lh_2;
9347 } else {
9348 switch (halign.value) {
9349 case 'left':
9350 lx1 = labelX - lw;
9351 lx2 = labelX;
9352 break;
9353
9354 case 'center':
9355 lx1 = labelX - lw_2;
9356 lx2 = labelX + lw_2;
9357 break;
9358
9359 case 'right':
9360 lx1 = labelX;
9361 lx2 = labelX + lw;
9362 break;
9363 }
9364
9365 switch (valign.value) {
9366 case 'top':
9367 ly1 = labelY - lh;
9368 ly2 = labelY;
9369 break;
9370
9371 case 'center':
9372 ly1 = labelY - lh_2;
9373 ly2 = labelY + lh_2;
9374 break;
9375
9376 case 'bottom':
9377 ly1 = labelY;
9378 ly2 = labelY + lh;
9379 break;
9380 }
9381 } // shift by margin and expand by outline and border
9382
9383
9384 lx1 += marginX - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9385 lx2 += marginX + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError;
9386 ly1 += marginY - Math.max(outlineWidth, halfBorderWidth) - padding - marginOfError;
9387 ly2 += marginY + Math.max(outlineWidth, halfBorderWidth) + padding + marginOfError; // always store the unrotated label bounds separately
9388
9389 var bbPrefix = prefix || 'main';
9390 var bbs = _p.labelBounds;
9391 var bb = bbs[bbPrefix] = bbs[bbPrefix] || {};
9392 bb.x1 = lx1;
9393 bb.y1 = ly1;
9394 bb.x2 = lx2;
9395 bb.y2 = ly2;
9396 bb.w = lx2 - lx1;
9397 bb.h = ly2 - ly1;
9398 var isAutorotate = isEdge && rotation.strValue === 'autorotate';
9399 var isPfValue = rotation.pfValue != null && rotation.pfValue !== 0;
9400
9401 if (isAutorotate || isPfValue) {
9402 var theta = isAutorotate ? prefixedProperty(_p.rstyle, 'labelAngle', prefix) : rotation.pfValue;
9403 var cos = Math.cos(theta);
9404 var sin = Math.sin(theta); // rotation point (default value for center-center)
9405
9406 var xo = (lx1 + lx2) / 2;
9407 var yo = (ly1 + ly2) / 2;
9408
9409 if (!isEdge) {
9410 switch (halign.value) {
9411 case 'left':
9412 xo = lx2;
9413 break;
9414
9415 case 'right':
9416 xo = lx1;
9417 break;
9418 }
9419
9420 switch (valign.value) {
9421 case 'top':
9422 yo = ly2;
9423 break;
9424
9425 case 'bottom':
9426 yo = ly1;
9427 break;
9428 }
9429 }
9430
9431 var rotate = function rotate(x, y) {
9432 x = x - xo;
9433 y = y - yo;
9434 return {
9435 x: x * cos - y * sin + xo,
9436 y: x * sin + y * cos + yo
9437 };
9438 };
9439
9440 var px1y1 = rotate(lx1, ly1);
9441 var px1y2 = rotate(lx1, ly2);
9442 var px2y1 = rotate(lx2, ly1);
9443 var px2y2 = rotate(lx2, ly2);
9444 lx1 = Math.min(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9445 lx2 = Math.max(px1y1.x, px1y2.x, px2y1.x, px2y2.x);
9446 ly1 = Math.min(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9447 ly2 = Math.max(px1y1.y, px1y2.y, px2y1.y, px2y2.y);
9448 }
9449
9450 var bbPrefixRot = bbPrefix + 'Rot';
9451 var bbRot = bbs[bbPrefixRot] = bbs[bbPrefixRot] || {};
9452 bbRot.x1 = lx1;
9453 bbRot.y1 = ly1;
9454 bbRot.x2 = lx2;
9455 bbRot.y2 = ly2;
9456 bbRot.w = lx2 - lx1;
9457 bbRot.h = ly2 - ly1;
9458 updateBounds(bounds, lx1, ly1, lx2, ly2);
9459 updateBounds(_p.labelBounds.all, lx1, ly1, lx2, ly2);
9460 }
9461
9462 return bounds;
9463}; // get the bounding box of the elements (in raw model position)
9464
9465
9466var boundingBoxImpl = function boundingBoxImpl(ele, options) {
9467 var cy = ele._private.cy;
9468 var styleEnabled = cy.styleEnabled();
9469 var headless = cy.headless();
9470 var bounds = makeBoundingBox();
9471 var _p = ele._private;
9472 var isNode = ele.isNode();
9473 var isEdge = ele.isEdge();
9474 var ex1, ex2, ey1, ey2; // extrema of body / lines
9475
9476 var x, y; // node pos
9477
9478 var rstyle = _p.rstyle;
9479 var manualExpansion = isNode && styleEnabled ? ele.pstyle('bounds-expansion').pfValue : [0]; // must use `display` prop only, as reading `compound.width()` causes recursion
9480 // (other factors like width values will be considered later in this function anyway)
9481
9482 var isDisplayed = function isDisplayed(ele) {
9483 return ele.pstyle('display').value !== 'none';
9484 };
9485
9486 var displayed = !styleEnabled || isDisplayed(ele) // must take into account connected nodes b/c of implicit edge hiding on display:none node
9487 && (!isEdge || isDisplayed(ele.source()) && isDisplayed(ele.target()));
9488
9489 if (displayed) {
9490 // displayed suffices, since we will find zero area eles anyway
9491 var overlayOpacity = 0;
9492 var overlayPadding = 0;
9493
9494 if (styleEnabled && options.includeOverlays) {
9495 overlayOpacity = ele.pstyle('overlay-opacity').value;
9496
9497 if (overlayOpacity !== 0) {
9498 overlayPadding = ele.pstyle('overlay-padding').value;
9499 }
9500 }
9501
9502 var w = 0;
9503 var wHalf = 0;
9504
9505 if (styleEnabled) {
9506 w = ele.pstyle('width').pfValue;
9507 wHalf = w / 2;
9508 }
9509
9510 if (isNode && options.includeNodes) {
9511 var pos = ele.position();
9512 x = pos.x;
9513 y = pos.y;
9514
9515 var _w = ele.outerWidth();
9516
9517 var halfW = _w / 2;
9518 var h = ele.outerHeight();
9519 var halfH = h / 2; // handle node dimensions
9520 /////////////////////////
9521
9522 ex1 = x - halfW;
9523 ex2 = x + halfW;
9524 ey1 = y - halfH;
9525 ey2 = y + halfH;
9526 updateBounds(bounds, ex1, ey1, ex2, ey2);
9527 } else if (isEdge && options.includeEdges) {
9528 if (styleEnabled && !headless) {
9529 var curveStyle = ele.pstyle('curve-style').strValue; // handle edge dimensions (rough box estimate)
9530 //////////////////////////////////////////////
9531
9532 ex1 = Math.min(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9533 ex2 = Math.max(rstyle.srcX, rstyle.midX, rstyle.tgtX);
9534 ey1 = Math.min(rstyle.srcY, rstyle.midY, rstyle.tgtY);
9535 ey2 = Math.max(rstyle.srcY, rstyle.midY, rstyle.tgtY); // take into account edge width
9536
9537 ex1 -= wHalf;
9538 ex2 += wHalf;
9539 ey1 -= wHalf;
9540 ey2 += wHalf;
9541 updateBounds(bounds, ex1, ey1, ex2, ey2); // precise edges
9542 ////////////////
9543
9544 if (curveStyle === 'haystack') {
9545 var hpts = rstyle.haystackPts;
9546
9547 if (hpts && hpts.length === 2) {
9548 ex1 = hpts[0].x;
9549 ey1 = hpts[0].y;
9550 ex2 = hpts[1].x;
9551 ey2 = hpts[1].y;
9552
9553 if (ex1 > ex2) {
9554 var temp = ex1;
9555 ex1 = ex2;
9556 ex2 = temp;
9557 }
9558
9559 if (ey1 > ey2) {
9560 var _temp = ey1;
9561 ey1 = ey2;
9562 ey2 = _temp;
9563 }
9564
9565 updateBounds(bounds, ex1 - wHalf, ey1 - wHalf, ex2 + wHalf, ey2 + wHalf);
9566 }
9567 } else if (curveStyle === 'bezier' || curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'taxi') {
9568 var pts;
9569
9570 switch (curveStyle) {
9571 case 'bezier':
9572 case 'unbundled-bezier':
9573 pts = rstyle.bezierPts;
9574 break;
9575
9576 case 'segments':
9577 case 'taxi':
9578 pts = rstyle.linePts;
9579 break;
9580 }
9581
9582 if (pts != null) {
9583 for (var j = 0; j < pts.length; j++) {
9584 var pt = pts[j];
9585 ex1 = pt.x - wHalf;
9586 ex2 = pt.x + wHalf;
9587 ey1 = pt.y - wHalf;
9588 ey2 = pt.y + wHalf;
9589 updateBounds(bounds, ex1, ey1, ex2, ey2);
9590 }
9591 }
9592 } // bezier-like or segment-like edge
9593
9594 } else {
9595 // headless or style disabled
9596 // fallback on source and target positions
9597 //////////////////////////////////////////
9598 var n1 = ele.source();
9599 var n1pos = n1.position();
9600 var n2 = ele.target();
9601 var n2pos = n2.position();
9602 ex1 = n1pos.x;
9603 ex2 = n2pos.x;
9604 ey1 = n1pos.y;
9605 ey2 = n2pos.y;
9606
9607 if (ex1 > ex2) {
9608 var _temp2 = ex1;
9609 ex1 = ex2;
9610 ex2 = _temp2;
9611 }
9612
9613 if (ey1 > ey2) {
9614 var _temp3 = ey1;
9615 ey1 = ey2;
9616 ey2 = _temp3;
9617 } // take into account edge width
9618
9619
9620 ex1 -= wHalf;
9621 ex2 += wHalf;
9622 ey1 -= wHalf;
9623 ey2 += wHalf;
9624 updateBounds(bounds, ex1, ey1, ex2, ey2);
9625 } // headless or style disabled
9626
9627 } // edges
9628 // handle edge arrow size
9629 /////////////////////////
9630
9631
9632 if (styleEnabled && options.includeEdges && isEdge) {
9633 updateBoundsFromArrow(bounds, ele, 'mid-source');
9634 updateBoundsFromArrow(bounds, ele, 'mid-target');
9635 updateBoundsFromArrow(bounds, ele, 'source');
9636 updateBoundsFromArrow(bounds, ele, 'target');
9637 } // ghost
9638 ////////
9639
9640
9641 if (styleEnabled) {
9642 var ghost = ele.pstyle('ghost').value === 'yes';
9643
9644 if (ghost) {
9645 var gx = ele.pstyle('ghost-offset-x').pfValue;
9646 var gy = ele.pstyle('ghost-offset-y').pfValue;
9647 updateBounds(bounds, bounds.x1 + gx, bounds.y1 + gy, bounds.x2 + gx, bounds.y2 + gy);
9648 }
9649 } // always store the body bounds separately from the labels
9650
9651
9652 var bbBody = _p.bodyBounds = _p.bodyBounds || {};
9653 assignBoundingBox(bbBody, bounds);
9654 expandBoundingBoxSides(bbBody, manualExpansion);
9655 expandBoundingBox(bbBody, 1); // expand to work around browser dimension inaccuracies
9656 // overlay
9657 //////////
9658
9659 if (styleEnabled) {
9660 ex1 = bounds.x1;
9661 ex2 = bounds.x2;
9662 ey1 = bounds.y1;
9663 ey2 = bounds.y2;
9664 updateBounds(bounds, ex1 - overlayPadding, ey1 - overlayPadding, ex2 + overlayPadding, ey2 + overlayPadding);
9665 } // always store the body bounds separately from the labels
9666
9667
9668 var bbOverlay = _p.overlayBounds = _p.overlayBounds || {};
9669 assignBoundingBox(bbOverlay, bounds);
9670 expandBoundingBoxSides(bbOverlay, manualExpansion);
9671 expandBoundingBox(bbOverlay, 1); // expand to work around browser dimension inaccuracies
9672 // handle label dimensions
9673 //////////////////////////
9674
9675 var bbLabels = _p.labelBounds = _p.labelBounds || {};
9676
9677 if (bbLabels.all != null) {
9678 clearBoundingBox(bbLabels.all);
9679 } else {
9680 bbLabels.all = makeBoundingBox();
9681 }
9682
9683 if (styleEnabled && options.includeLabels) {
9684 if (options.includeMainLabels) {
9685 updateBoundsFromLabel(bounds, ele, null);
9686 }
9687
9688 if (isEdge) {
9689 if (options.includeSourceLabels) {
9690 updateBoundsFromLabel(bounds, ele, 'source');
9691 }
9692
9693 if (options.includeTargetLabels) {
9694 updateBoundsFromLabel(bounds, ele, 'target');
9695 }
9696 }
9697 } // style enabled for labels
9698
9699 } // if displayed
9700
9701
9702 bounds.x1 = noninf(bounds.x1);
9703 bounds.y1 = noninf(bounds.y1);
9704 bounds.x2 = noninf(bounds.x2);
9705 bounds.y2 = noninf(bounds.y2);
9706 bounds.w = noninf(bounds.x2 - bounds.x1);
9707 bounds.h = noninf(bounds.y2 - bounds.y1);
9708
9709 if (bounds.w > 0 && bounds.h > 0 && displayed) {
9710 expandBoundingBoxSides(bounds, manualExpansion); // expand bounds by 1 because antialiasing can increase the visual/effective size by 1 on all sides
9711
9712 expandBoundingBox(bounds, 1);
9713 }
9714
9715 return bounds;
9716};
9717
9718var getKey = function getKey(opts) {
9719 var i = 0;
9720
9721 var tf = function tf(val) {
9722 return (val ? 1 : 0) << i++;
9723 };
9724
9725 var key = 0;
9726 key += tf(opts.incudeNodes);
9727 key += tf(opts.includeEdges);
9728 key += tf(opts.includeLabels);
9729 key += tf(opts.includeMainLabels);
9730 key += tf(opts.includeSourceLabels);
9731 key += tf(opts.includeTargetLabels);
9732 key += tf(opts.includeOverlays);
9733 return key;
9734};
9735
9736var getBoundingBoxPosKey = function getBoundingBoxPosKey(ele) {
9737 if (ele.isEdge()) {
9738 var p1 = ele.source().position();
9739 var p2 = ele.target().position();
9740
9741 var r = function r(x) {
9742 return Math.round(x);
9743 };
9744
9745 return hashIntsArray([r(p1.x), r(p1.y), r(p2.x), r(p2.y)]);
9746 } else {
9747 return 0;
9748 }
9749};
9750
9751var cachedBoundingBoxImpl = function cachedBoundingBoxImpl(ele, opts) {
9752 var _p = ele._private;
9753 var bb;
9754 var isEdge = ele.isEdge();
9755 var key = opts == null ? defBbOptsKey : getKey(opts);
9756 var usingDefOpts = key === defBbOptsKey;
9757 var currPosKey = getBoundingBoxPosKey(ele);
9758 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9759 var useCache = opts.useCache && isPosKeySame;
9760
9761 var isDirty = function isDirty(ele) {
9762 return ele._private.bbCache == null || ele._private.styleDirty;
9763 };
9764
9765 var needRecalc = !useCache || isDirty(ele) || isEdge && isDirty(ele.source()) || isDirty(ele.target());
9766
9767 if (needRecalc) {
9768 if (!isPosKeySame) {
9769 ele.recalculateRenderedStyle(useCache);
9770 }
9771
9772 bb = boundingBoxImpl(ele, defBbOpts);
9773 _p.bbCache = bb;
9774 _p.bbCachePosKey = currPosKey;
9775 } else {
9776 bb = _p.bbCache;
9777 } // not using def opts => need to build up bb from combination of sub bbs
9778
9779
9780 if (!usingDefOpts) {
9781 var isNode = ele.isNode();
9782 bb = makeBoundingBox();
9783
9784 if (opts.includeNodes && isNode || opts.includeEdges && !isNode) {
9785 if (opts.includeOverlays) {
9786 updateBoundsFromBox(bb, _p.overlayBounds);
9787 } else {
9788 updateBoundsFromBox(bb, _p.bodyBounds);
9789 }
9790 }
9791
9792 if (opts.includeLabels) {
9793 if (opts.includeMainLabels && (!isEdge || opts.includeSourceLabels && opts.includeTargetLabels)) {
9794 updateBoundsFromBox(bb, _p.labelBounds.all);
9795 } else {
9796 if (opts.includeMainLabels) {
9797 updateBoundsFromBox(bb, _p.labelBounds.mainRot);
9798 }
9799
9800 if (opts.includeSourceLabels) {
9801 updateBoundsFromBox(bb, _p.labelBounds.sourceRot);
9802 }
9803
9804 if (opts.includeTargetLabels) {
9805 updateBoundsFromBox(bb, _p.labelBounds.targetRot);
9806 }
9807 }
9808 }
9809
9810 bb.w = bb.x2 - bb.x1;
9811 bb.h = bb.y2 - bb.y1;
9812 }
9813
9814 return bb;
9815};
9816
9817var defBbOpts = {
9818 includeNodes: true,
9819 includeEdges: true,
9820 includeLabels: true,
9821 includeMainLabels: true,
9822 includeSourceLabels: true,
9823 includeTargetLabels: true,
9824 includeOverlays: true,
9825 useCache: true
9826};
9827var defBbOptsKey = getKey(defBbOpts);
9828var filledBbOpts = defaults(defBbOpts);
9829
9830elesfn$k.boundingBox = function (options) {
9831 var bounds; // the main usecase is ele.boundingBox() for a single element with no/def options
9832 // specified s.t. the cache is used, so check for this case to make it faster by
9833 // avoiding the overhead of the rest of the function
9834
9835 if (this.length === 1 && this[0]._private.bbCache != null && !this[0]._private.styleDirty && (options === undefined || options.useCache === undefined || options.useCache === true)) {
9836 if (options === undefined) {
9837 options = defBbOpts;
9838 } else {
9839 options = filledBbOpts(options);
9840 }
9841
9842 bounds = cachedBoundingBoxImpl(this[0], options);
9843 } else {
9844 bounds = makeBoundingBox();
9845 options = options || defBbOpts;
9846 var opts = filledBbOpts(options);
9847 var eles = this;
9848 var cy = eles.cy();
9849 var styleEnabled = cy.styleEnabled();
9850
9851 if (styleEnabled) {
9852 for (var i = 0; i < eles.length; i++) {
9853 var ele = eles[i];
9854 var _p = ele._private;
9855 var currPosKey = getBoundingBoxPosKey(ele);
9856 var isPosKeySame = _p.bbCachePosKey === currPosKey;
9857 var useCache = opts.useCache && isPosKeySame && !_p.styleDirty;
9858 ele.recalculateRenderedStyle(useCache);
9859 }
9860 }
9861
9862 this.updateCompoundBounds(!options.useCache);
9863
9864 for (var _i = 0; _i < eles.length; _i++) {
9865 var _ele = eles[_i];
9866 updateBoundsFromBox(bounds, cachedBoundingBoxImpl(_ele, opts));
9867 }
9868 }
9869
9870 bounds.x1 = noninf(bounds.x1);
9871 bounds.y1 = noninf(bounds.y1);
9872 bounds.x2 = noninf(bounds.x2);
9873 bounds.y2 = noninf(bounds.y2);
9874 bounds.w = noninf(bounds.x2 - bounds.x1);
9875 bounds.h = noninf(bounds.y2 - bounds.y1);
9876 return bounds;
9877};
9878
9879elesfn$k.dirtyBoundingBoxCache = function () {
9880 for (var i = 0; i < this.length; i++) {
9881 var _p = this[i]._private;
9882 _p.bbCache = null;
9883 _p.bbCachePosKey = null;
9884 _p.bodyBounds = null;
9885 _p.overlayBounds = null;
9886 _p.labelBounds.all = null;
9887 _p.labelBounds.source = null;
9888 _p.labelBounds.target = null;
9889 _p.labelBounds.main = null;
9890 _p.labelBounds.sourceRot = null;
9891 _p.labelBounds.targetRot = null;
9892 _p.labelBounds.mainRot = null;
9893 _p.arrowBounds.source = null;
9894 _p.arrowBounds.target = null;
9895 _p.arrowBounds['mid-source'] = null;
9896 _p.arrowBounds['mid-target'] = null;
9897 }
9898
9899 this.emitAndNotify('bounds');
9900 return this;
9901}; // private helper to get bounding box for custom node positions
9902// - good for perf in certain cases but currently requires dirtying the rendered style
9903// - would be better to not modify the nodes but the nodes are read directly everywhere in the renderer...
9904// - try to use for only things like discrete layouts where the node position would change anyway
9905
9906
9907elesfn$k.boundingBoxAt = function (fn) {
9908 var nodes = this.nodes();
9909 var cy = this.cy();
9910 var hasCompoundNodes = cy.hasCompoundNodes();
9911 var parents = cy.collection();
9912
9913 if (hasCompoundNodes) {
9914 parents = nodes.filter(function (node) {
9915 return node.isParent();
9916 });
9917 nodes = nodes.not(parents);
9918 }
9919
9920 if (plainObject(fn)) {
9921 var obj = fn;
9922
9923 fn = function fn() {
9924 return obj;
9925 };
9926 }
9927
9928 var storeOldPos = function storeOldPos(node, i) {
9929 return node._private.bbAtOldPos = fn(node, i);
9930 };
9931
9932 var getOldPos = function getOldPos(node) {
9933 return node._private.bbAtOldPos;
9934 };
9935
9936 cy.startBatch();
9937 nodes.forEach(storeOldPos).silentPositions(fn);
9938
9939 if (hasCompoundNodes) {
9940 parents.dirtyCompoundBoundsCache();
9941 parents.dirtyBoundingBoxCache();
9942 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
9943 }
9944
9945 var bb = copyBoundingBox(this.boundingBox({
9946 useCache: false
9947 }));
9948 nodes.silentPositions(getOldPos);
9949
9950 if (hasCompoundNodes) {
9951 parents.dirtyCompoundBoundsCache();
9952 parents.dirtyBoundingBoxCache();
9953 parents.updateCompoundBounds(true); // force update b/c we're inside a batch cycle
9954 }
9955
9956 cy.endBatch();
9957 return bb;
9958};
9959
9960fn$3.boundingbox = fn$3.bb = fn$3.boundingBox;
9961fn$3.renderedBoundingbox = fn$3.renderedBoundingBox;
9962var bounds = elesfn$k;
9963
9964var fn$4, elesfn$l;
9965fn$4 = elesfn$l = {};
9966
9967var defineDimFns = function defineDimFns(opts) {
9968 opts.uppercaseName = capitalize(opts.name);
9969 opts.autoName = 'auto' + opts.uppercaseName;
9970 opts.labelName = 'label' + opts.uppercaseName;
9971 opts.outerName = 'outer' + opts.uppercaseName;
9972 opts.uppercaseOuterName = capitalize(opts.outerName);
9973
9974 fn$4[opts.name] = function dimImpl() {
9975 var ele = this[0];
9976 var _p = ele._private;
9977 var cy = _p.cy;
9978 var styleEnabled = cy._private.styleEnabled;
9979
9980 if (ele) {
9981 if (styleEnabled) {
9982 if (ele.isParent()) {
9983 ele.updateCompoundBounds();
9984 return _p[opts.autoName] || 0;
9985 }
9986
9987 var d = ele.pstyle(opts.name);
9988
9989 switch (d.strValue) {
9990 case 'label':
9991 ele.recalculateRenderedStyle();
9992 return _p.rstyle[opts.labelName] || 0;
9993
9994 default:
9995 return d.pfValue;
9996 }
9997 } else {
9998 return 1;
9999 }
10000 }
10001 };
10002
10003 fn$4['outer' + opts.uppercaseName] = function outerDimImpl() {
10004 var ele = this[0];
10005 var _p = ele._private;
10006 var cy = _p.cy;
10007 var styleEnabled = cy._private.styleEnabled;
10008
10009 if (ele) {
10010 if (styleEnabled) {
10011 var dim = ele[opts.name]();
10012 var border = ele.pstyle('border-width').pfValue; // n.b. 1/2 each side
10013
10014 var padding = 2 * ele.padding();
10015 return dim + border + padding;
10016 } else {
10017 return 1;
10018 }
10019 }
10020 };
10021
10022 fn$4['rendered' + opts.uppercaseName] = function renderedDimImpl() {
10023 var ele = this[0];
10024
10025 if (ele) {
10026 var d = ele[opts.name]();
10027 return d * this.cy().zoom();
10028 }
10029 };
10030
10031 fn$4['rendered' + opts.uppercaseOuterName] = function renderedOuterDimImpl() {
10032 var ele = this[0];
10033
10034 if (ele) {
10035 var od = ele[opts.outerName]();
10036 return od * this.cy().zoom();
10037 }
10038 };
10039};
10040
10041defineDimFns({
10042 name: 'width'
10043});
10044defineDimFns({
10045 name: 'height'
10046});
10047
10048elesfn$l.padding = function () {
10049 var ele = this[0];
10050 var _p = ele._private;
10051
10052 if (ele.isParent()) {
10053 ele.updateCompoundBounds();
10054
10055 if (_p.autoPadding !== undefined) {
10056 return _p.autoPadding;
10057 } else {
10058 return ele.pstyle('padding').pfValue;
10059 }
10060 } else {
10061 return ele.pstyle('padding').pfValue;
10062 }
10063};
10064
10065elesfn$l.paddedHeight = function () {
10066 var ele = this[0];
10067 return ele.height() + 2 * ele.padding();
10068};
10069
10070elesfn$l.paddedWidth = function () {
10071 var ele = this[0];
10072 return ele.width() + 2 * ele.padding();
10073};
10074
10075var widthHeight = elesfn$l;
10076
10077var ifEdge = function ifEdge(ele, getValue) {
10078 if (ele.isEdge()) {
10079 return getValue(ele);
10080 }
10081};
10082
10083var ifEdgeRenderedPosition = function ifEdgeRenderedPosition(ele, getPoint) {
10084 if (ele.isEdge()) {
10085 var cy = ele.cy();
10086 return modelToRenderedPosition(getPoint(ele), cy.zoom(), cy.pan());
10087 }
10088};
10089
10090var ifEdgeRenderedPositions = function ifEdgeRenderedPositions(ele, getPoints) {
10091 if (ele.isEdge()) {
10092 var cy = ele.cy();
10093 var pan = cy.pan();
10094 var zoom = cy.zoom();
10095 return getPoints(ele).map(function (p) {
10096 return modelToRenderedPosition(p, zoom, pan);
10097 });
10098 }
10099};
10100
10101var controlPoints = function controlPoints(ele) {
10102 return ele.renderer().getControlPoints(ele);
10103};
10104
10105var segmentPoints = function segmentPoints(ele) {
10106 return ele.renderer().getSegmentPoints(ele);
10107};
10108
10109var sourceEndpoint = function sourceEndpoint(ele) {
10110 return ele.renderer().getSourceEndpoint(ele);
10111};
10112
10113var targetEndpoint = function targetEndpoint(ele) {
10114 return ele.renderer().getTargetEndpoint(ele);
10115};
10116
10117var midpoint = function midpoint(ele) {
10118 return ele.renderer().getEdgeMidpoint(ele);
10119};
10120
10121var pts = {
10122 controlPoints: {
10123 get: controlPoints,
10124 mult: true
10125 },
10126 segmentPoints: {
10127 get: segmentPoints,
10128 mult: true
10129 },
10130 sourceEndpoint: {
10131 get: sourceEndpoint
10132 },
10133 targetEndpoint: {
10134 get: targetEndpoint
10135 },
10136 midpoint: {
10137 get: midpoint
10138 }
10139};
10140
10141var renderedName = function renderedName(name) {
10142 return 'rendered' + name[0].toUpperCase() + name.substr(1);
10143};
10144
10145var edgePoints = Object.keys(pts).reduce(function (obj, name) {
10146 var spec = pts[name];
10147 var rName = renderedName(name);
10148
10149 obj[name] = function () {
10150 return ifEdge(this, spec.get);
10151 };
10152
10153 if (spec.mult) {
10154 obj[rName] = function () {
10155 return ifEdgeRenderedPositions(this, spec.get);
10156 };
10157 } else {
10158 obj[rName] = function () {
10159 return ifEdgeRenderedPosition(this, spec.get);
10160 };
10161 }
10162
10163 return obj;
10164}, {});
10165
10166var dimensions = extend({}, position, bounds, widthHeight, edgePoints);
10167
10168/*!
10169Event object based on jQuery events, MIT license
10170
10171https://jquery.org/license/
10172https://tldrlegal.com/license/mit-license
10173https://github.com/jquery/jquery/blob/master/src/event.js
10174*/
10175var Event = function Event(src, props) {
10176 this.recycle(src, props);
10177};
10178
10179function returnFalse() {
10180 return false;
10181}
10182
10183function returnTrue() {
10184 return true;
10185} // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
10186
10187
10188Event.prototype = {
10189 instanceString: function instanceString() {
10190 return 'event';
10191 },
10192 recycle: function recycle(src, props) {
10193 this.isImmediatePropagationStopped = this.isPropagationStopped = this.isDefaultPrevented = returnFalse;
10194
10195 if (src != null && src.preventDefault) {
10196 // Browser Event object
10197 this.type = src.type; // Events bubbling up the document may have been marked as prevented
10198 // by a handler lower down the tree; reflect the correct value.
10199
10200 this.isDefaultPrevented = src.defaultPrevented ? returnTrue : returnFalse;
10201 } else if (src != null && src.type) {
10202 // Plain object containing all event details
10203 props = src;
10204 } else {
10205 // Event string
10206 this.type = src;
10207 } // Put explicitly provided properties onto the event object
10208
10209
10210 if (props != null) {
10211 // more efficient to manually copy fields we use
10212 this.originalEvent = props.originalEvent;
10213 this.type = props.type != null ? props.type : this.type;
10214 this.cy = props.cy;
10215 this.target = props.target;
10216 this.position = props.position;
10217 this.renderedPosition = props.renderedPosition;
10218 this.namespace = props.namespace;
10219 this.layout = props.layout;
10220 }
10221
10222 if (this.cy != null && this.position != null && this.renderedPosition == null) {
10223 // create a rendered position based on the passed position
10224 var pos = this.position;
10225 var zoom = this.cy.zoom();
10226 var pan = this.cy.pan();
10227 this.renderedPosition = {
10228 x: pos.x * zoom + pan.x,
10229 y: pos.y * zoom + pan.y
10230 };
10231 } // Create a timestamp if incoming event doesn't have one
10232
10233
10234 this.timeStamp = src && src.timeStamp || Date.now();
10235 },
10236 preventDefault: function preventDefault() {
10237 this.isDefaultPrevented = returnTrue;
10238 var e = this.originalEvent;
10239
10240 if (!e) {
10241 return;
10242 } // if preventDefault exists run it on the original event
10243
10244
10245 if (e.preventDefault) {
10246 e.preventDefault();
10247 }
10248 },
10249 stopPropagation: function stopPropagation() {
10250 this.isPropagationStopped = returnTrue;
10251 var e = this.originalEvent;
10252
10253 if (!e) {
10254 return;
10255 } // if stopPropagation exists run it on the original event
10256
10257
10258 if (e.stopPropagation) {
10259 e.stopPropagation();
10260 }
10261 },
10262 stopImmediatePropagation: function stopImmediatePropagation() {
10263 this.isImmediatePropagationStopped = returnTrue;
10264 this.stopPropagation();
10265 },
10266 isDefaultPrevented: returnFalse,
10267 isPropagationStopped: returnFalse,
10268 isImmediatePropagationStopped: returnFalse
10269};
10270
10271var eventRegex = /^([^.]+)(\.(?:[^.]+))?$/; // regex for matching event strings (e.g. "click.namespace")
10272
10273var universalNamespace = '.*'; // matches as if no namespace specified and prevents users from unbinding accidentally
10274
10275var defaults$8 = {
10276 qualifierCompare: function qualifierCompare(q1, q2) {
10277 return q1 === q2;
10278 },
10279 eventMatches: function eventMatches()
10280 /*context, listener, eventObj*/
10281 {
10282 return true;
10283 },
10284 addEventFields: function addEventFields()
10285 /*context, evt*/
10286 {},
10287 callbackContext: function callbackContext(context
10288 /*, listener, eventObj*/
10289 ) {
10290 return context;
10291 },
10292 beforeEmit: function beforeEmit()
10293 /* context, listener, eventObj */
10294 {},
10295 afterEmit: function afterEmit()
10296 /* context, listener, eventObj */
10297 {},
10298 bubble: function bubble()
10299 /*context*/
10300 {
10301 return false;
10302 },
10303 parent: function parent()
10304 /*context*/
10305 {
10306 return null;
10307 },
10308 context: null
10309};
10310var defaultsKeys = Object.keys(defaults$8);
10311var emptyOpts = {};
10312
10313function Emitter() {
10314 var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : emptyOpts;
10315 var context = arguments.length > 1 ? arguments[1] : undefined;
10316
10317 // micro-optimisation vs Object.assign() -- reduces Element instantiation time
10318 for (var i = 0; i < defaultsKeys.length; i++) {
10319 var key = defaultsKeys[i];
10320 this[key] = opts[key] || defaults$8[key];
10321 }
10322
10323 this.context = context || this.context;
10324 this.listeners = [];
10325 this.emitting = 0;
10326}
10327
10328var p = Emitter.prototype;
10329
10330var forEachEvent = function forEachEvent(self, handler, events, qualifier, callback, conf, confOverrides) {
10331 if (fn(qualifier)) {
10332 callback = qualifier;
10333 qualifier = null;
10334 }
10335
10336 if (confOverrides) {
10337 if (conf == null) {
10338 conf = confOverrides;
10339 } else {
10340 conf = extend({}, conf, confOverrides);
10341 }
10342 }
10343
10344 var eventList = array(events) ? events : events.split(/\s+/);
10345
10346 for (var i = 0; i < eventList.length; i++) {
10347 var evt = eventList[i];
10348
10349 if (emptyString(evt)) {
10350 continue;
10351 }
10352
10353 var match = evt.match(eventRegex); // type[.namespace]
10354
10355 if (match) {
10356 var type = match[1];
10357 var namespace = match[2] ? match[2] : null;
10358 var ret = handler(self, evt, type, namespace, qualifier, callback, conf);
10359
10360 if (ret === false) {
10361 break;
10362 } // allow exiting early
10363
10364 }
10365 }
10366};
10367
10368var makeEventObj = function makeEventObj(self, obj) {
10369 self.addEventFields(self.context, obj);
10370 return new Event(obj.type, obj);
10371};
10372
10373var forEachEventObj = function forEachEventObj(self, handler, events) {
10374 if (event(events)) {
10375 handler(self, events);
10376 return;
10377 } else if (plainObject(events)) {
10378 handler(self, makeEventObj(self, events));
10379 return;
10380 }
10381
10382 var eventList = array(events) ? events : events.split(/\s+/);
10383
10384 for (var i = 0; i < eventList.length; i++) {
10385 var evt = eventList[i];
10386
10387 if (emptyString(evt)) {
10388 continue;
10389 }
10390
10391 var match = evt.match(eventRegex); // type[.namespace]
10392
10393 if (match) {
10394 var type = match[1];
10395 var namespace = match[2] ? match[2] : null;
10396 var eventObj = makeEventObj(self, {
10397 type: type,
10398 namespace: namespace,
10399 target: self.context
10400 });
10401 handler(self, eventObj);
10402 }
10403 }
10404};
10405
10406p.on = p.addListener = function (events, qualifier, callback, conf, confOverrides) {
10407 forEachEvent(this, function (self, event, type, namespace, qualifier, callback, conf) {
10408 if (fn(callback)) {
10409 self.listeners.push({
10410 event: event,
10411 // full event string
10412 callback: callback,
10413 // callback to run
10414 type: type,
10415 // the event type (e.g. 'click')
10416 namespace: namespace,
10417 // the event namespace (e.g. ".foo")
10418 qualifier: qualifier,
10419 // a restriction on whether to match this emitter
10420 conf: conf // additional configuration
10421
10422 });
10423 }
10424 }, events, qualifier, callback, conf, confOverrides);
10425 return this;
10426};
10427
10428p.one = function (events, qualifier, callback, conf) {
10429 return this.on(events, qualifier, callback, conf, {
10430 one: true
10431 });
10432};
10433
10434p.removeListener = p.off = function (events, qualifier, callback, conf) {
10435 var _this = this;
10436
10437 if (this.emitting !== 0) {
10438 this.listeners = copyArray(this.listeners);
10439 }
10440
10441 var listeners = this.listeners;
10442
10443 var _loop = function _loop(i) {
10444 var listener = listeners[i];
10445 forEachEvent(_this, function (self, event, type, namespace, qualifier, callback
10446 /*, conf*/
10447 ) {
10448 if ((listener.type === type || events === '*') && (!namespace && listener.namespace !== '.*' || listener.namespace === namespace) && (!qualifier || self.qualifierCompare(listener.qualifier, qualifier)) && (!callback || listener.callback === callback)) {
10449 listeners.splice(i, 1);
10450 return false;
10451 }
10452 }, events, qualifier, callback, conf);
10453 };
10454
10455 for (var i = listeners.length - 1; i >= 0; i--) {
10456 _loop(i);
10457 }
10458
10459 return this;
10460};
10461
10462p.removeAllListeners = function () {
10463 return this.removeListener('*');
10464};
10465
10466p.emit = p.trigger = function (events, extraParams, manualCallback) {
10467 var listeners = this.listeners;
10468 var numListenersBeforeEmit = listeners.length;
10469 this.emitting++;
10470
10471 if (!array(extraParams)) {
10472 extraParams = [extraParams];
10473 }
10474
10475 forEachEventObj(this, function (self, eventObj) {
10476 if (manualCallback != null) {
10477 listeners = [{
10478 event: eventObj.event,
10479 type: eventObj.type,
10480 namespace: eventObj.namespace,
10481 callback: manualCallback
10482 }];
10483 numListenersBeforeEmit = listeners.length;
10484 }
10485
10486 var _loop2 = function _loop2(i) {
10487 var listener = listeners[i];
10488
10489 if (listener.type === eventObj.type && (!listener.namespace || listener.namespace === eventObj.namespace || listener.namespace === universalNamespace) && self.eventMatches(self.context, listener, eventObj)) {
10490 var args = [eventObj];
10491
10492 if (extraParams != null) {
10493 push(args, extraParams);
10494 }
10495
10496 self.beforeEmit(self.context, listener, eventObj);
10497
10498 if (listener.conf && listener.conf.one) {
10499 self.listeners = self.listeners.filter(function (l) {
10500 return l !== listener;
10501 });
10502 }
10503
10504 var context = self.callbackContext(self.context, listener, eventObj);
10505 var ret = listener.callback.apply(context, args);
10506 self.afterEmit(self.context, listener, eventObj);
10507
10508 if (ret === false) {
10509 eventObj.stopPropagation();
10510 eventObj.preventDefault();
10511 }
10512 } // if listener matches
10513
10514 };
10515
10516 for (var i = 0; i < numListenersBeforeEmit; i++) {
10517 _loop2(i);
10518 } // for listener
10519
10520
10521 if (self.bubble(self.context) && !eventObj.isPropagationStopped()) {
10522 self.parent(self.context).emit(eventObj, extraParams);
10523 }
10524 }, events);
10525 this.emitting--;
10526 return this;
10527};
10528
10529var emitterOptions = {
10530 qualifierCompare: function qualifierCompare(selector1, selector2) {
10531 if (selector1 == null || selector2 == null) {
10532 return selector1 == null && selector2 == null;
10533 } else {
10534 return selector1.sameText(selector2);
10535 }
10536 },
10537 eventMatches: function eventMatches(ele, listener, eventObj) {
10538 var selector = listener.qualifier;
10539
10540 if (selector != null) {
10541 return ele !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
10542 }
10543
10544 return true;
10545 },
10546 addEventFields: function addEventFields(ele, evt) {
10547 evt.cy = ele.cy();
10548 evt.target = ele;
10549 },
10550 callbackContext: function callbackContext(ele, listener, eventObj) {
10551 return listener.qualifier != null ? eventObj.target : ele;
10552 },
10553 beforeEmit: function beforeEmit(context, listener
10554 /*, eventObj*/
10555 ) {
10556 if (listener.conf && listener.conf.once) {
10557 listener.conf.onceCollection.removeListener(listener.event, listener.qualifier, listener.callback);
10558 }
10559 },
10560 bubble: function bubble() {
10561 return true;
10562 },
10563 parent: function parent(ele) {
10564 return ele.isChild() ? ele.parent() : ele.cy();
10565 }
10566};
10567
10568var argSelector = function argSelector(arg) {
10569 if (string(arg)) {
10570 return new Selector(arg);
10571 } else {
10572 return arg;
10573 }
10574};
10575
10576var elesfn$m = {
10577 createEmitter: function createEmitter() {
10578 for (var i = 0; i < this.length; i++) {
10579 var ele = this[i];
10580 var _p = ele._private;
10581
10582 if (!_p.emitter) {
10583 _p.emitter = new Emitter(emitterOptions, ele);
10584 }
10585 }
10586
10587 return this;
10588 },
10589 emitter: function emitter() {
10590 return this._private.emitter;
10591 },
10592 on: function on(events, selector, callback) {
10593 var argSel = argSelector(selector);
10594
10595 for (var i = 0; i < this.length; i++) {
10596 var ele = this[i];
10597 ele.emitter().on(events, argSel, callback);
10598 }
10599
10600 return this;
10601 },
10602 removeListener: function removeListener(events, selector, callback) {
10603 var argSel = argSelector(selector);
10604
10605 for (var i = 0; i < this.length; i++) {
10606 var ele = this[i];
10607 ele.emitter().removeListener(events, argSel, callback);
10608 }
10609
10610 return this;
10611 },
10612 removeAllListeners: function removeAllListeners() {
10613 for (var i = 0; i < this.length; i++) {
10614 var ele = this[i];
10615 ele.emitter().removeAllListeners();
10616 }
10617
10618 return this;
10619 },
10620 one: function one(events, selector, callback) {
10621 var argSel = argSelector(selector);
10622
10623 for (var i = 0; i < this.length; i++) {
10624 var ele = this[i];
10625 ele.emitter().one(events, argSel, callback);
10626 }
10627
10628 return this;
10629 },
10630 once: function once(events, selector, callback) {
10631 var argSel = argSelector(selector);
10632
10633 for (var i = 0; i < this.length; i++) {
10634 var ele = this[i];
10635 ele.emitter().on(events, argSel, callback, {
10636 once: true,
10637 onceCollection: this
10638 });
10639 }
10640 },
10641 emit: function emit(events, extraParams) {
10642 for (var i = 0; i < this.length; i++) {
10643 var ele = this[i];
10644 ele.emitter().emit(events, extraParams);
10645 }
10646
10647 return this;
10648 },
10649 emitAndNotify: function emitAndNotify(event, extraParams) {
10650 // for internal use only
10651 if (this.length === 0) {
10652 return;
10653 } // empty collections don't need to notify anything
10654 // notify renderer
10655
10656
10657 this.cy().notify(event, this);
10658 this.emit(event, extraParams);
10659 return this;
10660 }
10661};
10662define$3.eventAliasesOn(elesfn$m);
10663
10664var elesfn$n = {
10665 nodes: function nodes(selector) {
10666 return this.filter(function (ele) {
10667 return ele.isNode();
10668 }).filter(selector);
10669 },
10670 edges: function edges(selector) {
10671 return this.filter(function (ele) {
10672 return ele.isEdge();
10673 }).filter(selector);
10674 },
10675 // internal helper to get nodes and edges as separate collections with single iteration over elements
10676 byGroup: function byGroup() {
10677 var nodes = this.spawn();
10678 var edges = this.spawn();
10679
10680 for (var i = 0; i < this.length; i++) {
10681 var ele = this[i];
10682
10683 if (ele.isNode()) {
10684 nodes.push(ele);
10685 } else {
10686 edges.push(ele);
10687 }
10688 }
10689
10690 return {
10691 nodes: nodes,
10692 edges: edges
10693 };
10694 },
10695 filter: function filter(_filter, thisArg) {
10696 if (_filter === undefined) {
10697 // check this first b/c it's the most common/performant case
10698 return this;
10699 } else if (string(_filter) || elementOrCollection(_filter)) {
10700 return new Selector(_filter).filter(this);
10701 } else if (fn(_filter)) {
10702 var filterEles = this.spawn();
10703 var eles = this;
10704
10705 for (var i = 0; i < eles.length; i++) {
10706 var ele = eles[i];
10707 var include = thisArg ? _filter.apply(thisArg, [ele, i, eles]) : _filter(ele, i, eles);
10708
10709 if (include) {
10710 filterEles.push(ele);
10711 }
10712 }
10713
10714 return filterEles;
10715 }
10716
10717 return this.spawn(); // if not handled by above, give 'em an empty collection
10718 },
10719 not: function not(toRemove) {
10720 if (!toRemove) {
10721 return this;
10722 } else {
10723 if (string(toRemove)) {
10724 toRemove = this.filter(toRemove);
10725 }
10726
10727 var elements = this.spawn();
10728
10729 for (var i = 0; i < this.length; i++) {
10730 var element = this[i];
10731 var remove = toRemove.has(element);
10732
10733 if (!remove) {
10734 elements.push(element);
10735 }
10736 }
10737
10738 return elements;
10739 }
10740 },
10741 absoluteComplement: function absoluteComplement() {
10742 var cy = this.cy();
10743 return cy.mutableElements().not(this);
10744 },
10745 intersect: function intersect(other) {
10746 // if a selector is specified, then filter by it instead
10747 if (string(other)) {
10748 var selector = other;
10749 return this.filter(selector);
10750 }
10751
10752 var elements = this.spawn();
10753 var col1 = this;
10754 var col2 = other;
10755 var col1Smaller = this.length < other.length;
10756 var colS = col1Smaller ? col1 : col2;
10757 var colL = col1Smaller ? col2 : col1;
10758
10759 for (var i = 0; i < colS.length; i++) {
10760 var ele = colS[i];
10761
10762 if (colL.has(ele)) {
10763 elements.push(ele);
10764 }
10765 }
10766
10767 return elements;
10768 },
10769 xor: function xor(other) {
10770 var cy = this._private.cy;
10771
10772 if (string(other)) {
10773 other = cy.$(other);
10774 }
10775
10776 var elements = this.spawn();
10777 var col1 = this;
10778 var col2 = other;
10779
10780 var add = function add(col, other) {
10781 for (var i = 0; i < col.length; i++) {
10782 var ele = col[i];
10783 var id = ele._private.data.id;
10784 var inOther = other.hasElementWithId(id);
10785
10786 if (!inOther) {
10787 elements.push(ele);
10788 }
10789 }
10790 };
10791
10792 add(col1, col2);
10793 add(col2, col1);
10794 return elements;
10795 },
10796 diff: function diff(other) {
10797 var cy = this._private.cy;
10798
10799 if (string(other)) {
10800 other = cy.$(other);
10801 }
10802
10803 var left = this.spawn();
10804 var right = this.spawn();
10805 var both = this.spawn();
10806 var col1 = this;
10807 var col2 = other;
10808
10809 var add = function add(col, other, retEles) {
10810 for (var i = 0; i < col.length; i++) {
10811 var ele = col[i];
10812 var id = ele._private.data.id;
10813 var inOther = other.hasElementWithId(id);
10814
10815 if (inOther) {
10816 both.merge(ele);
10817 } else {
10818 retEles.push(ele);
10819 }
10820 }
10821 };
10822
10823 add(col1, col2, left);
10824 add(col2, col1, right);
10825 return {
10826 left: left,
10827 right: right,
10828 both: both
10829 };
10830 },
10831 add: function add(toAdd) {
10832 var cy = this._private.cy;
10833
10834 if (!toAdd) {
10835 return this;
10836 }
10837
10838 if (string(toAdd)) {
10839 var selector = toAdd;
10840 toAdd = cy.mutableElements().filter(selector);
10841 }
10842
10843 var elements = this.spawnSelf();
10844
10845 for (var i = 0; i < toAdd.length; i++) {
10846 var ele = toAdd[i];
10847 var add = !this.has(ele);
10848
10849 if (add) {
10850 elements.push(ele);
10851 }
10852 }
10853
10854 return elements;
10855 },
10856 // in place merge on calling collection
10857 merge: function merge(toAdd) {
10858 var _p = this._private;
10859 var cy = _p.cy;
10860
10861 if (!toAdd) {
10862 return this;
10863 }
10864
10865 if (toAdd && string(toAdd)) {
10866 var selector = toAdd;
10867 toAdd = cy.mutableElements().filter(selector);
10868 }
10869
10870 var map = _p.map;
10871
10872 for (var i = 0; i < toAdd.length; i++) {
10873 var toAddEle = toAdd[i];
10874 var id = toAddEle._private.data.id;
10875 var add = !map.has(id);
10876
10877 if (add) {
10878 var index = this.length++;
10879 this[index] = toAddEle;
10880 map.set(id, {
10881 ele: toAddEle,
10882 index: index
10883 });
10884 }
10885 }
10886
10887 return this; // chaining
10888 },
10889 unmergeAt: function unmergeAt(i) {
10890 var ele = this[i];
10891 var id = ele.id();
10892 var _p = this._private;
10893 var map = _p.map; // remove ele
10894
10895 this[i] = undefined;
10896 map["delete"](id);
10897 var unmergedLastEle = i === this.length - 1; // replace empty spot with last ele in collection
10898
10899 if (this.length > 1 && !unmergedLastEle) {
10900 var lastEleI = this.length - 1;
10901 var lastEle = this[lastEleI];
10902 var lastEleId = lastEle._private.data.id;
10903 this[lastEleI] = undefined;
10904 this[i] = lastEle;
10905 map.set(lastEleId, {
10906 ele: lastEle,
10907 index: i
10908 });
10909 } // the collection is now 1 ele smaller
10910
10911
10912 this.length--;
10913 return this;
10914 },
10915 // remove single ele in place in calling collection
10916 unmergeOne: function unmergeOne(ele) {
10917 ele = ele[0];
10918 var _p = this._private;
10919 var id = ele._private.data.id;
10920 var map = _p.map;
10921 var entry = map.get(id);
10922
10923 if (!entry) {
10924 return this; // no need to remove
10925 }
10926
10927 var i = entry.index;
10928 this.unmergeAt(i);
10929 return this;
10930 },
10931 // remove eles in place on calling collection
10932 unmerge: function unmerge(toRemove) {
10933 var cy = this._private.cy;
10934
10935 if (!toRemove) {
10936 return this;
10937 }
10938
10939 if (toRemove && string(toRemove)) {
10940 var selector = toRemove;
10941 toRemove = cy.mutableElements().filter(selector);
10942 }
10943
10944 for (var i = 0; i < toRemove.length; i++) {
10945 this.unmergeOne(toRemove[i]);
10946 }
10947
10948 return this; // chaining
10949 },
10950 unmergeBy: function unmergeBy(toRmFn) {
10951 for (var i = this.length - 1; i >= 0; i--) {
10952 var ele = this[i];
10953
10954 if (toRmFn(ele)) {
10955 this.unmergeAt(i);
10956 }
10957 }
10958
10959 return this;
10960 },
10961 map: function map(mapFn, thisArg) {
10962 var arr = [];
10963 var eles = this;
10964
10965 for (var i = 0; i < eles.length; i++) {
10966 var ele = eles[i];
10967 var ret = thisArg ? mapFn.apply(thisArg, [ele, i, eles]) : mapFn(ele, i, eles);
10968 arr.push(ret);
10969 }
10970
10971 return arr;
10972 },
10973 reduce: function reduce(fn, initialValue) {
10974 var val = initialValue;
10975 var eles = this;
10976
10977 for (var i = 0; i < eles.length; i++) {
10978 val = fn(val, eles[i], i, eles);
10979 }
10980
10981 return val;
10982 },
10983 max: function max(valFn, thisArg) {
10984 var max = -Infinity;
10985 var maxEle;
10986 var eles = this;
10987
10988 for (var i = 0; i < eles.length; i++) {
10989 var ele = eles[i];
10990 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
10991
10992 if (val > max) {
10993 max = val;
10994 maxEle = ele;
10995 }
10996 }
10997
10998 return {
10999 value: max,
11000 ele: maxEle
11001 };
11002 },
11003 min: function min(valFn, thisArg) {
11004 var min = Infinity;
11005 var minEle;
11006 var eles = this;
11007
11008 for (var i = 0; i < eles.length; i++) {
11009 var ele = eles[i];
11010 var val = thisArg ? valFn.apply(thisArg, [ele, i, eles]) : valFn(ele, i, eles);
11011
11012 if (val < min) {
11013 min = val;
11014 minEle = ele;
11015 }
11016 }
11017
11018 return {
11019 value: min,
11020 ele: minEle
11021 };
11022 }
11023}; // aliases
11024
11025var fn$5 = elesfn$n;
11026fn$5['u'] = fn$5['|'] = fn$5['+'] = fn$5.union = fn$5.or = fn$5.add;
11027fn$5['\\'] = fn$5['!'] = fn$5['-'] = fn$5.difference = fn$5.relativeComplement = fn$5.subtract = fn$5.not;
11028fn$5['n'] = fn$5['&'] = fn$5['.'] = fn$5.and = fn$5.intersection = fn$5.intersect;
11029fn$5['^'] = fn$5['(+)'] = fn$5['(-)'] = fn$5.symmetricDifference = fn$5.symdiff = fn$5.xor;
11030fn$5.fnFilter = fn$5.filterFn = fn$5.stdFilter = fn$5.filter;
11031fn$5.complement = fn$5.abscomp = fn$5.absoluteComplement;
11032
11033var elesfn$o = {
11034 isNode: function isNode() {
11035 return this.group() === 'nodes';
11036 },
11037 isEdge: function isEdge() {
11038 return this.group() === 'edges';
11039 },
11040 isLoop: function isLoop() {
11041 return this.isEdge() && this.source()[0] === this.target()[0];
11042 },
11043 isSimple: function isSimple() {
11044 return this.isEdge() && this.source()[0] !== this.target()[0];
11045 },
11046 group: function group() {
11047 var ele = this[0];
11048
11049 if (ele) {
11050 return ele._private.group;
11051 }
11052 }
11053};
11054
11055/**
11056 * Elements are drawn in a specific order based on compound depth (low to high), the element type (nodes above edges),
11057 * and z-index (low to high). These styles affect how this applies:
11058 *
11059 * z-compound-depth: May be `bottom | orphan | auto | top`. The first drawn is `bottom`, then `orphan` which is the
11060 * same depth as the root of the compound graph, followed by the default value `auto` which draws in order from
11061 * root to leaves of the compound graph. The last drawn is `top`.
11062 * z-index-compare: May be `auto | manual`. The default value is `auto` which always draws edges under nodes.
11063 * `manual` ignores this convention and draws based on the `z-index` value setting.
11064 * z-index: An integer value that affects the relative draw order of elements. In general, an element with a higher
11065 * `z-index` will be drawn on top of an element with a lower `z-index`.
11066 */
11067
11068var zIndexSort = function zIndexSort(a, b) {
11069 var cy = a.cy();
11070 var hasCompoundNodes = cy.hasCompoundNodes();
11071
11072 function getDepth(ele) {
11073 var style = ele.pstyle('z-compound-depth');
11074
11075 if (style.value === 'auto') {
11076 return hasCompoundNodes ? ele.zDepth() : 0;
11077 } else if (style.value === 'bottom') {
11078 return -1;
11079 } else if (style.value === 'top') {
11080 return MAX_INT;
11081 } // 'orphan'
11082
11083
11084 return 0;
11085 }
11086
11087 var depthDiff = getDepth(a) - getDepth(b);
11088
11089 if (depthDiff !== 0) {
11090 return depthDiff;
11091 }
11092
11093 function getEleDepth(ele) {
11094 var style = ele.pstyle('z-index-compare');
11095
11096 if (style.value === 'auto') {
11097 return ele.isNode() ? 1 : 0;
11098 } // 'manual'
11099
11100
11101 return 0;
11102 }
11103
11104 var eleDiff = getEleDepth(a) - getEleDepth(b);
11105
11106 if (eleDiff !== 0) {
11107 return eleDiff;
11108 }
11109
11110 var zDiff = a.pstyle('z-index').value - b.pstyle('z-index').value;
11111
11112 if (zDiff !== 0) {
11113 return zDiff;
11114 } // compare indices in the core (order added to graph w/ last on top)
11115
11116
11117 return a.poolIndex() - b.poolIndex();
11118};
11119
11120var elesfn$p = {
11121 forEach: function forEach(fn$1, thisArg) {
11122 if (fn(fn$1)) {
11123 var N = this.length;
11124
11125 for (var i = 0; i < N; i++) {
11126 var ele = this[i];
11127 var ret = thisArg ? fn$1.apply(thisArg, [ele, i, this]) : fn$1(ele, i, this);
11128
11129 if (ret === false) {
11130 break;
11131 } // exit each early on return false
11132
11133 }
11134 }
11135
11136 return this;
11137 },
11138 toArray: function toArray() {
11139 var array = [];
11140
11141 for (var i = 0; i < this.length; i++) {
11142 array.push(this[i]);
11143 }
11144
11145 return array;
11146 },
11147 slice: function slice(start, end) {
11148 var array = [];
11149 var thisSize = this.length;
11150
11151 if (end == null) {
11152 end = thisSize;
11153 }
11154
11155 if (start == null) {
11156 start = 0;
11157 }
11158
11159 if (start < 0) {
11160 start = thisSize + start;
11161 }
11162
11163 if (end < 0) {
11164 end = thisSize + end;
11165 }
11166
11167 for (var i = start; i >= 0 && i < end && i < thisSize; i++) {
11168 array.push(this[i]);
11169 }
11170
11171 return this.spawn(array);
11172 },
11173 size: function size() {
11174 return this.length;
11175 },
11176 eq: function eq(i) {
11177 return this[i] || this.spawn();
11178 },
11179 first: function first() {
11180 return this[0] || this.spawn();
11181 },
11182 last: function last() {
11183 return this[this.length - 1] || this.spawn();
11184 },
11185 empty: function empty() {
11186 return this.length === 0;
11187 },
11188 nonempty: function nonempty() {
11189 return !this.empty();
11190 },
11191 sort: function sort(sortFn) {
11192 if (!fn(sortFn)) {
11193 return this;
11194 }
11195
11196 var sorted = this.toArray().sort(sortFn);
11197 return this.spawn(sorted);
11198 },
11199 sortByZIndex: function sortByZIndex() {
11200 return this.sort(zIndexSort);
11201 },
11202 zDepth: function zDepth() {
11203 var ele = this[0];
11204
11205 if (!ele) {
11206 return undefined;
11207 } // let cy = ele.cy();
11208
11209
11210 var _p = ele._private;
11211 var group = _p.group;
11212
11213 if (group === 'nodes') {
11214 var depth = _p.data.parent ? ele.parents().size() : 0;
11215
11216 if (!ele.isParent()) {
11217 return MAX_INT - 1; // childless nodes always on top
11218 }
11219
11220 return depth;
11221 } else {
11222 var src = _p.source;
11223 var tgt = _p.target;
11224 var srcDepth = src.zDepth();
11225 var tgtDepth = tgt.zDepth();
11226 return Math.max(srcDepth, tgtDepth, 0); // depth of deepest parent
11227 }
11228 }
11229};
11230elesfn$p.each = elesfn$p.forEach;
11231
11232var defineSymbolIterator = function defineSymbolIterator() {
11233 var typeofUndef = "undefined" ;
11234 var isIteratorSupported = (typeof Symbol === "undefined" ? "undefined" : _typeof(Symbol)) != typeofUndef && _typeof(Symbol.iterator) != typeofUndef; // eslint-disable-line no-undef
11235
11236 if (isIteratorSupported) {
11237 elesfn$p[Symbol.iterator] = function () {
11238 var _this = this;
11239
11240 // eslint-disable-line no-undef
11241 var entry = {
11242 value: undefined,
11243 done: false
11244 };
11245 var i = 0;
11246 var length = this.length;
11247 return _defineProperty({
11248 next: function next() {
11249 if (i < length) {
11250 entry.value = _this[i++];
11251 } else {
11252 entry.value = undefined;
11253 entry.done = true;
11254 }
11255
11256 return entry;
11257 }
11258 }, Symbol.iterator, function () {
11259 // eslint-disable-line no-undef
11260 return this;
11261 });
11262 };
11263 }
11264};
11265
11266defineSymbolIterator();
11267
11268var getLayoutDimensionOptions = defaults({
11269 nodeDimensionsIncludeLabels: false
11270});
11271var elesfn$q = {
11272 // Calculates and returns node dimensions { x, y } based on options given
11273 layoutDimensions: function layoutDimensions(options) {
11274 options = getLayoutDimensionOptions(options);
11275 var dims;
11276
11277 if (!this.takesUpSpace()) {
11278 dims = {
11279 w: 0,
11280 h: 0
11281 };
11282 } else if (options.nodeDimensionsIncludeLabels) {
11283 var bbDim = this.boundingBox();
11284 dims = {
11285 w: bbDim.w,
11286 h: bbDim.h
11287 };
11288 } else {
11289 dims = {
11290 w: this.outerWidth(),
11291 h: this.outerHeight()
11292 };
11293 } // sanitise the dimensions for external layouts (avoid division by zero)
11294
11295
11296 if (dims.w === 0 || dims.h === 0) {
11297 dims.w = dims.h = 1;
11298 }
11299
11300 return dims;
11301 },
11302 // using standard layout options, apply position function (w/ or w/o animation)
11303 layoutPositions: function layoutPositions(layout, options, fn) {
11304 var nodes = this.nodes().filter(function (n) {
11305 return !n.isParent();
11306 });
11307 var cy = this.cy();
11308 var layoutEles = options.eles; // nodes & edges
11309
11310 var getMemoizeKey = function getMemoizeKey(node) {
11311 return node.id();
11312 };
11313
11314 var fnMem = memoize(fn, getMemoizeKey); // memoized version of position function
11315
11316 layout.emit({
11317 type: 'layoutstart',
11318 layout: layout
11319 });
11320 layout.animations = [];
11321
11322 var calculateSpacing = function calculateSpacing(spacing, nodesBb, pos) {
11323 var center = {
11324 x: nodesBb.x1 + nodesBb.w / 2,
11325 y: nodesBb.y1 + nodesBb.h / 2
11326 };
11327 var spacingVector = {
11328 // scale from center of bounding box (not necessarily 0,0)
11329 x: (pos.x - center.x) * spacing,
11330 y: (pos.y - center.y) * spacing
11331 };
11332 return {
11333 x: center.x + spacingVector.x,
11334 y: center.y + spacingVector.y
11335 };
11336 };
11337
11338 var useSpacingFactor = options.spacingFactor && options.spacingFactor !== 1;
11339
11340 var spacingBb = function spacingBb() {
11341 if (!useSpacingFactor) {
11342 return null;
11343 }
11344
11345 var bb = makeBoundingBox();
11346
11347 for (var i = 0; i < nodes.length; i++) {
11348 var node = nodes[i];
11349 var pos = fnMem(node, i);
11350 expandBoundingBoxByPoint(bb, pos.x, pos.y);
11351 }
11352
11353 return bb;
11354 };
11355
11356 var bb = spacingBb();
11357 var getFinalPos = memoize(function (node, i) {
11358 var newPos = fnMem(node, i);
11359
11360 if (useSpacingFactor) {
11361 var spacing = Math.abs(options.spacingFactor);
11362 newPos = calculateSpacing(spacing, bb, newPos);
11363 }
11364
11365 if (options.transform != null) {
11366 newPos = options.transform(node, newPos);
11367 }
11368
11369 return newPos;
11370 }, getMemoizeKey);
11371
11372 if (options.animate) {
11373 for (var i = 0; i < nodes.length; i++) {
11374 var node = nodes[i];
11375 var newPos = getFinalPos(node, i);
11376 var animateNode = options.animateFilter == null || options.animateFilter(node, i);
11377
11378 if (animateNode) {
11379 var ani = node.animation({
11380 position: newPos,
11381 duration: options.animationDuration,
11382 easing: options.animationEasing
11383 });
11384 layout.animations.push(ani);
11385 } else {
11386 node.position(newPos);
11387 }
11388 }
11389
11390 if (options.fit) {
11391 var fitAni = cy.animation({
11392 fit: {
11393 boundingBox: layoutEles.boundingBoxAt(getFinalPos),
11394 padding: options.padding
11395 },
11396 duration: options.animationDuration,
11397 easing: options.animationEasing
11398 });
11399 layout.animations.push(fitAni);
11400 } else if (options.zoom !== undefined && options.pan !== undefined) {
11401 var zoomPanAni = cy.animation({
11402 zoom: options.zoom,
11403 pan: options.pan,
11404 duration: options.animationDuration,
11405 easing: options.animationEasing
11406 });
11407 layout.animations.push(zoomPanAni);
11408 }
11409
11410 layout.animations.forEach(function (ani) {
11411 return ani.play();
11412 });
11413 layout.one('layoutready', options.ready);
11414 layout.emit({
11415 type: 'layoutready',
11416 layout: layout
11417 });
11418 Promise$1.all(layout.animations.map(function (ani) {
11419 return ani.promise();
11420 })).then(function () {
11421 layout.one('layoutstop', options.stop);
11422 layout.emit({
11423 type: 'layoutstop',
11424 layout: layout
11425 });
11426 });
11427 } else {
11428 nodes.positions(getFinalPos);
11429
11430 if (options.fit) {
11431 cy.fit(options.eles, options.padding);
11432 }
11433
11434 if (options.zoom != null) {
11435 cy.zoom(options.zoom);
11436 }
11437
11438 if (options.pan) {
11439 cy.pan(options.pan);
11440 }
11441
11442 layout.one('layoutready', options.ready);
11443 layout.emit({
11444 type: 'layoutready',
11445 layout: layout
11446 });
11447 layout.one('layoutstop', options.stop);
11448 layout.emit({
11449 type: 'layoutstop',
11450 layout: layout
11451 });
11452 }
11453
11454 return this; // chaining
11455 },
11456 layout: function layout(options) {
11457 var cy = this.cy();
11458 return cy.makeLayout(extend({}, options, {
11459 eles: this
11460 }));
11461 }
11462}; // aliases:
11463
11464elesfn$q.createLayout = elesfn$q.makeLayout = elesfn$q.layout;
11465
11466function styleCache(key, fn, ele) {
11467 var _p = ele._private;
11468 var cache = _p.styleCache = _p.styleCache || [];
11469 var val;
11470
11471 if ((val = cache[key]) != null) {
11472 return val;
11473 } else {
11474 val = cache[key] = fn(ele);
11475 return val;
11476 }
11477}
11478
11479function cacheStyleFunction(key, fn) {
11480 key = hashString(key);
11481 return function cachedStyleFunction(ele) {
11482 return styleCache(key, fn, ele);
11483 };
11484}
11485
11486function cachePrototypeStyleFunction(key, fn) {
11487 key = hashString(key);
11488
11489 var selfFn = function selfFn(ele) {
11490 return fn.call(ele);
11491 };
11492
11493 return function cachedPrototypeStyleFunction() {
11494 var ele = this[0];
11495
11496 if (ele) {
11497 return styleCache(key, selfFn, ele);
11498 }
11499 };
11500}
11501
11502var elesfn$r = {
11503 recalculateRenderedStyle: function recalculateRenderedStyle(useCache) {
11504 var cy = this.cy();
11505 var renderer = cy.renderer();
11506 var styleEnabled = cy.styleEnabled();
11507
11508 if (renderer && styleEnabled) {
11509 renderer.recalculateRenderedStyle(this, useCache);
11510 }
11511
11512 return this;
11513 },
11514 dirtyStyleCache: function dirtyStyleCache() {
11515 var cy = this.cy();
11516
11517 var dirty = function dirty(ele) {
11518 return ele._private.styleCache = null;
11519 };
11520
11521 if (cy.hasCompoundNodes()) {
11522 var eles;
11523 eles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11524 eles.merge(eles.connectedEdges());
11525 eles.forEach(dirty);
11526 } else {
11527 this.forEach(function (ele) {
11528 dirty(ele);
11529 ele.connectedEdges().forEach(dirty);
11530 });
11531 }
11532
11533 return this;
11534 },
11535 // fully updates (recalculates) the style for the elements
11536 updateStyle: function updateStyle(notifyRenderer) {
11537 var cy = this._private.cy;
11538
11539 if (!cy.styleEnabled()) {
11540 return this;
11541 }
11542
11543 if (cy.batching()) {
11544 var bEles = cy._private.batchStyleEles;
11545 bEles.merge(this);
11546 return this; // chaining and exit early when batching
11547 }
11548
11549 var hasCompounds = cy.hasCompoundNodes();
11550 var updatedEles = this;
11551 notifyRenderer = notifyRenderer || notifyRenderer === undefined ? true : false;
11552
11553 if (hasCompounds) {
11554 // then add everything up and down for compound selector checks
11555 updatedEles = this.spawnSelf().merge(this.descendants()).merge(this.parents());
11556 } // let changedEles = style.apply( updatedEles );
11557
11558
11559 var changedEles = updatedEles;
11560
11561 if (notifyRenderer) {
11562 changedEles.emitAndNotify('style'); // let renderer know we changed style
11563 } else {
11564 changedEles.emit('style'); // just fire the event
11565 }
11566
11567 updatedEles.forEach(function (ele) {
11568 return ele._private.styleDirty = true;
11569 });
11570 return this; // chaining
11571 },
11572 // private: clears dirty flag and recalculates style
11573 cleanStyle: function cleanStyle() {
11574 var cy = this.cy();
11575
11576 if (!cy.styleEnabled()) {
11577 return;
11578 }
11579
11580 for (var i = 0; i < this.length; i++) {
11581 var ele = this[i];
11582
11583 if (ele._private.styleDirty) {
11584 // n.b. this flag should be set before apply() to avoid potential infinite recursion
11585 ele._private.styleDirty = false;
11586 cy.style().apply(ele);
11587 }
11588 }
11589 },
11590 // get the internal parsed style object for the specified property
11591 parsedStyle: function parsedStyle(property) {
11592 var includeNonDefault = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
11593 var ele = this[0];
11594 var cy = ele.cy();
11595
11596 if (!cy.styleEnabled()) {
11597 return;
11598 }
11599
11600 if (ele) {
11601 this.cleanStyle();
11602 var overriddenStyle = ele._private.style[property];
11603
11604 if (overriddenStyle != null) {
11605 return overriddenStyle;
11606 } else if (includeNonDefault) {
11607 return cy.style().getDefaultProperty(property);
11608 } else {
11609 return null;
11610 }
11611 }
11612 },
11613 numericStyle: function numericStyle(property) {
11614 var ele = this[0];
11615
11616 if (!ele.cy().styleEnabled()) {
11617 return;
11618 }
11619
11620 if (ele) {
11621 var pstyle = ele.pstyle(property);
11622 return pstyle.pfValue !== undefined ? pstyle.pfValue : pstyle.value;
11623 }
11624 },
11625 numericStyleUnits: function numericStyleUnits(property) {
11626 var ele = this[0];
11627
11628 if (!ele.cy().styleEnabled()) {
11629 return;
11630 }
11631
11632 if (ele) {
11633 return ele.pstyle(property).units;
11634 }
11635 },
11636 // get the specified css property as a rendered value (i.e. on-screen value)
11637 // or get the whole rendered style if no property specified (NB doesn't allow setting)
11638 renderedStyle: function renderedStyle(property) {
11639 var cy = this.cy();
11640
11641 if (!cy.styleEnabled()) {
11642 return this;
11643 }
11644
11645 var ele = this[0];
11646
11647 if (ele) {
11648 return cy.style().getRenderedStyle(ele, property);
11649 }
11650 },
11651 // read the calculated css style of the element or override the style (via a bypass)
11652 style: function style(name, value) {
11653 var cy = this.cy();
11654
11655 if (!cy.styleEnabled()) {
11656 return this;
11657 }
11658
11659 var updateTransitions = false;
11660 var style = cy.style();
11661
11662 if (plainObject(name)) {
11663 // then extend the bypass
11664 var props = name;
11665 style.applyBypass(this, props, updateTransitions);
11666 this.emitAndNotify('style'); // let the renderer know we've updated style
11667 } else if (string(name)) {
11668 if (value === undefined) {
11669 // then get the property from the style
11670 var ele = this[0];
11671
11672 if (ele) {
11673 return style.getStylePropertyValue(ele, name);
11674 } else {
11675 // empty collection => can't get any value
11676 return;
11677 }
11678 } else {
11679 // then set the bypass with the property value
11680 style.applyBypass(this, name, value, updateTransitions);
11681 this.emitAndNotify('style'); // let the renderer know we've updated style
11682 }
11683 } else if (name === undefined) {
11684 var _ele = this[0];
11685
11686 if (_ele) {
11687 return style.getRawStyle(_ele);
11688 } else {
11689 // empty collection => can't get any value
11690 return;
11691 }
11692 }
11693
11694 return this; // chaining
11695 },
11696 removeStyle: function removeStyle(names) {
11697 var cy = this.cy();
11698
11699 if (!cy.styleEnabled()) {
11700 return this;
11701 }
11702
11703 var updateTransitions = false;
11704 var style = cy.style();
11705 var eles = this;
11706
11707 if (names === undefined) {
11708 for (var i = 0; i < eles.length; i++) {
11709 var ele = eles[i];
11710 style.removeAllBypasses(ele, updateTransitions);
11711 }
11712 } else {
11713 names = names.split(/\s+/);
11714
11715 for (var _i = 0; _i < eles.length; _i++) {
11716 var _ele2 = eles[_i];
11717 style.removeBypasses(_ele2, names, updateTransitions);
11718 }
11719 }
11720
11721 this.emitAndNotify('style'); // let the renderer know we've updated style
11722
11723 return this; // chaining
11724 },
11725 show: function show() {
11726 this.css('display', 'element');
11727 return this; // chaining
11728 },
11729 hide: function hide() {
11730 this.css('display', 'none');
11731 return this; // chaining
11732 },
11733 effectiveOpacity: function effectiveOpacity() {
11734 var cy = this.cy();
11735
11736 if (!cy.styleEnabled()) {
11737 return 1;
11738 }
11739
11740 var hasCompoundNodes = cy.hasCompoundNodes();
11741 var ele = this[0];
11742
11743 if (ele) {
11744 var _p = ele._private;
11745 var parentOpacity = ele.pstyle('opacity').value;
11746
11747 if (!hasCompoundNodes) {
11748 return parentOpacity;
11749 }
11750
11751 var parents = !_p.data.parent ? null : ele.parents();
11752
11753 if (parents) {
11754 for (var i = 0; i < parents.length; i++) {
11755 var parent = parents[i];
11756 var opacity = parent.pstyle('opacity').value;
11757 parentOpacity = opacity * parentOpacity;
11758 }
11759 }
11760
11761 return parentOpacity;
11762 }
11763 },
11764 transparent: function transparent() {
11765 var cy = this.cy();
11766
11767 if (!cy.styleEnabled()) {
11768 return false;
11769 }
11770
11771 var ele = this[0];
11772 var hasCompoundNodes = ele.cy().hasCompoundNodes();
11773
11774 if (ele) {
11775 if (!hasCompoundNodes) {
11776 return ele.pstyle('opacity').value === 0;
11777 } else {
11778 return ele.effectiveOpacity() === 0;
11779 }
11780 }
11781 },
11782 backgrounding: function backgrounding() {
11783 var cy = this.cy();
11784
11785 if (!cy.styleEnabled()) {
11786 return false;
11787 }
11788
11789 var ele = this[0];
11790 return ele._private.backgrounding ? true : false;
11791 }
11792};
11793
11794function checkCompound(ele, parentOk) {
11795 var _p = ele._private;
11796 var parents = _p.data.parent ? ele.parents() : null;
11797
11798 if (parents) {
11799 for (var i = 0; i < parents.length; i++) {
11800 var parent = parents[i];
11801
11802 if (!parentOk(parent)) {
11803 return false;
11804 }
11805 }
11806 }
11807
11808 return true;
11809}
11810
11811function defineDerivedStateFunction(specs) {
11812 var ok = specs.ok;
11813 var edgeOkViaNode = specs.edgeOkViaNode || specs.ok;
11814 var parentOk = specs.parentOk || specs.ok;
11815 return function () {
11816 var cy = this.cy();
11817
11818 if (!cy.styleEnabled()) {
11819 return true;
11820 }
11821
11822 var ele = this[0];
11823 var hasCompoundNodes = cy.hasCompoundNodes();
11824
11825 if (ele) {
11826 var _p = ele._private;
11827
11828 if (!ok(ele)) {
11829 return false;
11830 }
11831
11832 if (ele.isNode()) {
11833 return !hasCompoundNodes || checkCompound(ele, parentOk);
11834 } else {
11835 var src = _p.source;
11836 var tgt = _p.target;
11837 return edgeOkViaNode(src) && (!hasCompoundNodes || checkCompound(src, edgeOkViaNode)) && (src === tgt || edgeOkViaNode(tgt) && (!hasCompoundNodes || checkCompound(tgt, edgeOkViaNode)));
11838 }
11839 }
11840 };
11841}
11842
11843var eleTakesUpSpace = cacheStyleFunction('eleTakesUpSpace', function (ele) {
11844 return ele.pstyle('display').value === 'element' && ele.width() !== 0 && (ele.isNode() ? ele.height() !== 0 : true);
11845});
11846elesfn$r.takesUpSpace = cachePrototypeStyleFunction('takesUpSpace', defineDerivedStateFunction({
11847 ok: eleTakesUpSpace
11848}));
11849var eleInteractive = cacheStyleFunction('eleInteractive', function (ele) {
11850 return ele.pstyle('events').value === 'yes' && ele.pstyle('visibility').value === 'visible' && eleTakesUpSpace(ele);
11851});
11852var parentInteractive = cacheStyleFunction('parentInteractive', function (parent) {
11853 return parent.pstyle('visibility').value === 'visible' && eleTakesUpSpace(parent);
11854});
11855elesfn$r.interactive = cachePrototypeStyleFunction('interactive', defineDerivedStateFunction({
11856 ok: eleInteractive,
11857 parentOk: parentInteractive,
11858 edgeOkViaNode: eleTakesUpSpace
11859}));
11860
11861elesfn$r.noninteractive = function () {
11862 var ele = this[0];
11863
11864 if (ele) {
11865 return !ele.interactive();
11866 }
11867};
11868
11869var eleVisible = cacheStyleFunction('eleVisible', function (ele) {
11870 return ele.pstyle('visibility').value === 'visible' && ele.pstyle('opacity').pfValue !== 0 && eleTakesUpSpace(ele);
11871});
11872var edgeVisibleViaNode = eleTakesUpSpace;
11873elesfn$r.visible = cachePrototypeStyleFunction('visible', defineDerivedStateFunction({
11874 ok: eleVisible,
11875 edgeOkViaNode: edgeVisibleViaNode
11876}));
11877
11878elesfn$r.hidden = function () {
11879 var ele = this[0];
11880
11881 if (ele) {
11882 return !ele.visible();
11883 }
11884};
11885
11886elesfn$r.isBundledBezier = cachePrototypeStyleFunction('isBundledBezier', function () {
11887 if (!this.cy().styleEnabled()) {
11888 return false;
11889 }
11890
11891 return !this.removed() && this.pstyle('curve-style').value === 'bezier' && this.takesUpSpace();
11892});
11893elesfn$r.bypass = elesfn$r.css = elesfn$r.style;
11894elesfn$r.renderedCss = elesfn$r.renderedStyle;
11895elesfn$r.removeBypass = elesfn$r.removeCss = elesfn$r.removeStyle;
11896elesfn$r.pstyle = elesfn$r.parsedStyle;
11897
11898var elesfn$s = {};
11899
11900function defineSwitchFunction(params) {
11901 return function () {
11902 var args = arguments;
11903 var changedEles = []; // e.g. cy.nodes().select( data, handler )
11904
11905 if (args.length === 2) {
11906 var data = args[0];
11907 var handler = args[1];
11908 this.on(params.event, data, handler);
11909 } // e.g. cy.nodes().select( handler )
11910 else if (args.length === 1 && fn(args[0])) {
11911 var _handler = args[0];
11912 this.on(params.event, _handler);
11913 } // e.g. cy.nodes().select()
11914 // e.g. (private) cy.nodes().select(['tapselect'])
11915 else if (args.length === 0 || args.length === 1 && array(args[0])) {
11916 var addlEvents = args.length === 1 ? args[0] : null;
11917
11918 for (var i = 0; i < this.length; i++) {
11919 var ele = this[i];
11920 var able = !params.ableField || ele._private[params.ableField];
11921 var changed = ele._private[params.field] != params.value;
11922
11923 if (params.overrideAble) {
11924 var overrideAble = params.overrideAble(ele);
11925
11926 if (overrideAble !== undefined) {
11927 able = overrideAble;
11928
11929 if (!overrideAble) {
11930 return this;
11931 } // to save cycles assume not able for all on override
11932
11933 }
11934 }
11935
11936 if (able) {
11937 ele._private[params.field] = params.value;
11938
11939 if (changed) {
11940 changedEles.push(ele);
11941 }
11942 }
11943 }
11944
11945 var changedColl = this.spawn(changedEles);
11946 changedColl.updateStyle(); // change of state => possible change of style
11947
11948 changedColl.emit(params.event);
11949
11950 if (addlEvents) {
11951 changedColl.emit(addlEvents);
11952 }
11953 }
11954
11955 return this;
11956 };
11957}
11958
11959function defineSwitchSet(params) {
11960 elesfn$s[params.field] = function () {
11961 var ele = this[0];
11962
11963 if (ele) {
11964 if (params.overrideField) {
11965 var val = params.overrideField(ele);
11966
11967 if (val !== undefined) {
11968 return val;
11969 }
11970 }
11971
11972 return ele._private[params.field];
11973 }
11974 };
11975
11976 elesfn$s[params.on] = defineSwitchFunction({
11977 event: params.on,
11978 field: params.field,
11979 ableField: params.ableField,
11980 overrideAble: params.overrideAble,
11981 value: true
11982 });
11983 elesfn$s[params.off] = defineSwitchFunction({
11984 event: params.off,
11985 field: params.field,
11986 ableField: params.ableField,
11987 overrideAble: params.overrideAble,
11988 value: false
11989 });
11990}
11991
11992defineSwitchSet({
11993 field: 'locked',
11994 overrideField: function overrideField(ele) {
11995 return ele.cy().autolock() ? true : undefined;
11996 },
11997 on: 'lock',
11998 off: 'unlock'
11999});
12000defineSwitchSet({
12001 field: 'grabbable',
12002 overrideField: function overrideField(ele) {
12003 return ele.cy().autoungrabify() || ele.pannable() ? false : undefined;
12004 },
12005 on: 'grabify',
12006 off: 'ungrabify'
12007});
12008defineSwitchSet({
12009 field: 'selected',
12010 ableField: 'selectable',
12011 overrideAble: function overrideAble(ele) {
12012 return ele.cy().autounselectify() ? false : undefined;
12013 },
12014 on: 'select',
12015 off: 'unselect'
12016});
12017defineSwitchSet({
12018 field: 'selectable',
12019 overrideField: function overrideField(ele) {
12020 return ele.cy().autounselectify() ? false : undefined;
12021 },
12022 on: 'selectify',
12023 off: 'unselectify'
12024});
12025elesfn$s.deselect = elesfn$s.unselect;
12026
12027elesfn$s.grabbed = function () {
12028 var ele = this[0];
12029
12030 if (ele) {
12031 return ele._private.grabbed;
12032 }
12033};
12034
12035defineSwitchSet({
12036 field: 'active',
12037 on: 'activate',
12038 off: 'unactivate'
12039});
12040defineSwitchSet({
12041 field: 'pannable',
12042 on: 'panify',
12043 off: 'unpanify'
12044});
12045
12046elesfn$s.inactive = function () {
12047 var ele = this[0];
12048
12049 if (ele) {
12050 return !ele._private.active;
12051 }
12052};
12053
12054var elesfn$t = {}; // DAG functions
12055////////////////
12056
12057var defineDagExtremity = function defineDagExtremity(params) {
12058 return function dagExtremityImpl(selector) {
12059 var eles = this;
12060 var ret = [];
12061
12062 for (var i = 0; i < eles.length; i++) {
12063 var ele = eles[i];
12064
12065 if (!ele.isNode()) {
12066 continue;
12067 }
12068
12069 var disqualified = false;
12070 var edges = ele.connectedEdges();
12071
12072 for (var j = 0; j < edges.length; j++) {
12073 var edge = edges[j];
12074 var src = edge.source();
12075 var tgt = edge.target();
12076
12077 if (params.noIncomingEdges && tgt === ele && src !== ele || params.noOutgoingEdges && src === ele && tgt !== ele) {
12078 disqualified = true;
12079 break;
12080 }
12081 }
12082
12083 if (!disqualified) {
12084 ret.push(ele);
12085 }
12086 }
12087
12088 return this.spawn(ret, true).filter(selector);
12089 };
12090};
12091
12092var defineDagOneHop = function defineDagOneHop(params) {
12093 return function (selector) {
12094 var eles = this;
12095 var oEles = [];
12096
12097 for (var i = 0; i < eles.length; i++) {
12098 var ele = eles[i];
12099
12100 if (!ele.isNode()) {
12101 continue;
12102 }
12103
12104 var edges = ele.connectedEdges();
12105
12106 for (var j = 0; j < edges.length; j++) {
12107 var edge = edges[j];
12108 var src = edge.source();
12109 var tgt = edge.target();
12110
12111 if (params.outgoing && src === ele) {
12112 oEles.push(edge);
12113 oEles.push(tgt);
12114 } else if (params.incoming && tgt === ele) {
12115 oEles.push(edge);
12116 oEles.push(src);
12117 }
12118 }
12119 }
12120
12121 return this.spawn(oEles, true).filter(selector);
12122 };
12123};
12124
12125var defineDagAllHops = function defineDagAllHops(params) {
12126 return function (selector) {
12127 var eles = this;
12128 var sEles = [];
12129 var sElesIds = {};
12130
12131 for (;;) {
12132 var next = params.outgoing ? eles.outgoers() : eles.incomers();
12133
12134 if (next.length === 0) {
12135 break;
12136 } // done if none left
12137
12138
12139 var newNext = false;
12140
12141 for (var i = 0; i < next.length; i++) {
12142 var n = next[i];
12143 var nid = n.id();
12144
12145 if (!sElesIds[nid]) {
12146 sElesIds[nid] = true;
12147 sEles.push(n);
12148 newNext = true;
12149 }
12150 }
12151
12152 if (!newNext) {
12153 break;
12154 } // done if touched all outgoers already
12155
12156
12157 eles = next;
12158 }
12159
12160 return this.spawn(sEles, true).filter(selector);
12161 };
12162};
12163
12164elesfn$t.clearTraversalCache = function () {
12165 for (var i = 0; i < this.length; i++) {
12166 this[i]._private.traversalCache = null;
12167 }
12168};
12169
12170extend(elesfn$t, {
12171 // get the root nodes in the DAG
12172 roots: defineDagExtremity({
12173 noIncomingEdges: true
12174 }),
12175 // get the leaf nodes in the DAG
12176 leaves: defineDagExtremity({
12177 noOutgoingEdges: true
12178 }),
12179 // normally called children in graph theory
12180 // these nodes =edges=> outgoing nodes
12181 outgoers: cache(defineDagOneHop({
12182 outgoing: true
12183 }), 'outgoers'),
12184 // aka DAG descendants
12185 successors: defineDagAllHops({
12186 outgoing: true
12187 }),
12188 // normally called parents in graph theory
12189 // these nodes <=edges= incoming nodes
12190 incomers: cache(defineDagOneHop({
12191 incoming: true
12192 }), 'incomers'),
12193 // aka DAG ancestors
12194 predecessors: defineDagAllHops({
12195 incoming: true
12196 })
12197}); // Neighbourhood functions
12198//////////////////////////
12199
12200extend(elesfn$t, {
12201 neighborhood: cache(function (selector) {
12202 var elements = [];
12203 var nodes = this.nodes();
12204
12205 for (var i = 0; i < nodes.length; i++) {
12206 // for all nodes
12207 var node = nodes[i];
12208 var connectedEdges = node.connectedEdges(); // for each connected edge, add the edge and the other node
12209
12210 for (var j = 0; j < connectedEdges.length; j++) {
12211 var edge = connectedEdges[j];
12212 var src = edge.source();
12213 var tgt = edge.target();
12214 var otherNode = node === src ? tgt : src; // need check in case of loop
12215
12216 if (otherNode.length > 0) {
12217 elements.push(otherNode[0]); // add node 1 hop away
12218 } // add connected edge
12219
12220
12221 elements.push(edge[0]);
12222 }
12223 }
12224
12225 return this.spawn(elements, true).filter(selector);
12226 }, 'neighborhood'),
12227 closedNeighborhood: function closedNeighborhood(selector) {
12228 return this.neighborhood().add(this).filter(selector);
12229 },
12230 openNeighborhood: function openNeighborhood(selector) {
12231 return this.neighborhood(selector);
12232 }
12233}); // aliases
12234
12235elesfn$t.neighbourhood = elesfn$t.neighborhood;
12236elesfn$t.closedNeighbourhood = elesfn$t.closedNeighborhood;
12237elesfn$t.openNeighbourhood = elesfn$t.openNeighborhood; // Edge functions
12238/////////////////
12239
12240extend(elesfn$t, {
12241 source: cache(function sourceImpl(selector) {
12242 var ele = this[0];
12243 var src;
12244
12245 if (ele) {
12246 src = ele._private.source || ele.cy().collection();
12247 }
12248
12249 return src && selector ? src.filter(selector) : src;
12250 }, 'source'),
12251 target: cache(function targetImpl(selector) {
12252 var ele = this[0];
12253 var tgt;
12254
12255 if (ele) {
12256 tgt = ele._private.target || ele.cy().collection();
12257 }
12258
12259 return tgt && selector ? tgt.filter(selector) : tgt;
12260 }, 'target'),
12261 sources: defineSourceFunction({
12262 attr: 'source'
12263 }),
12264 targets: defineSourceFunction({
12265 attr: 'target'
12266 })
12267});
12268
12269function defineSourceFunction(params) {
12270 return function sourceImpl(selector) {
12271 var sources = [];
12272
12273 for (var i = 0; i < this.length; i++) {
12274 var ele = this[i];
12275 var src = ele._private[params.attr];
12276
12277 if (src) {
12278 sources.push(src);
12279 }
12280 }
12281
12282 return this.spawn(sources, true).filter(selector);
12283 };
12284}
12285
12286extend(elesfn$t, {
12287 edgesWith: cache(defineEdgesWithFunction(), 'edgesWith'),
12288 edgesTo: cache(defineEdgesWithFunction({
12289 thisIsSrc: true
12290 }), 'edgesTo')
12291});
12292
12293function defineEdgesWithFunction(params) {
12294 return function edgesWithImpl(otherNodes) {
12295 var elements = [];
12296 var cy = this._private.cy;
12297 var p = params || {}; // get elements if a selector is specified
12298
12299 if (string(otherNodes)) {
12300 otherNodes = cy.$(otherNodes);
12301 }
12302
12303 for (var h = 0; h < otherNodes.length; h++) {
12304 var edges = otherNodes[h]._private.edges;
12305
12306 for (var i = 0; i < edges.length; i++) {
12307 var edge = edges[i];
12308 var edgeData = edge._private.data;
12309 var thisToOther = this.hasElementWithId(edgeData.source) && otherNodes.hasElementWithId(edgeData.target);
12310 var otherToThis = otherNodes.hasElementWithId(edgeData.source) && this.hasElementWithId(edgeData.target);
12311 var edgeConnectsThisAndOther = thisToOther || otherToThis;
12312
12313 if (!edgeConnectsThisAndOther) {
12314 continue;
12315 }
12316
12317 if (p.thisIsSrc || p.thisIsTgt) {
12318 if (p.thisIsSrc && !thisToOther) {
12319 continue;
12320 }
12321
12322 if (p.thisIsTgt && !otherToThis) {
12323 continue;
12324 }
12325 }
12326
12327 elements.push(edge);
12328 }
12329 }
12330
12331 return this.spawn(elements, true);
12332 };
12333}
12334
12335extend(elesfn$t, {
12336 connectedEdges: cache(function (selector) {
12337 var retEles = [];
12338 var eles = this;
12339
12340 for (var i = 0; i < eles.length; i++) {
12341 var node = eles[i];
12342
12343 if (!node.isNode()) {
12344 continue;
12345 }
12346
12347 var edges = node._private.edges;
12348
12349 for (var j = 0; j < edges.length; j++) {
12350 var edge = edges[j];
12351 retEles.push(edge);
12352 }
12353 }
12354
12355 return this.spawn(retEles, true).filter(selector);
12356 }, 'connectedEdges'),
12357 connectedNodes: cache(function (selector) {
12358 var retEles = [];
12359 var eles = this;
12360
12361 for (var i = 0; i < eles.length; i++) {
12362 var edge = eles[i];
12363
12364 if (!edge.isEdge()) {
12365 continue;
12366 }
12367
12368 retEles.push(edge.source()[0]);
12369 retEles.push(edge.target()[0]);
12370 }
12371
12372 return this.spawn(retEles, true).filter(selector);
12373 }, 'connectedNodes'),
12374 parallelEdges: cache(defineParallelEdgesFunction(), 'parallelEdges'),
12375 codirectedEdges: cache(defineParallelEdgesFunction({
12376 codirected: true
12377 }), 'codirectedEdges')
12378});
12379
12380function defineParallelEdgesFunction(params) {
12381 var defaults = {
12382 codirected: false
12383 };
12384 params = extend({}, defaults, params);
12385 return function parallelEdgesImpl(selector) {
12386 // micro-optimised for renderer
12387 var elements = [];
12388 var edges = this.edges();
12389 var p = params; // look at all the edges in the collection
12390
12391 for (var i = 0; i < edges.length; i++) {
12392 var edge1 = edges[i];
12393 var edge1_p = edge1._private;
12394 var src1 = edge1_p.source;
12395 var srcid1 = src1._private.data.id;
12396 var tgtid1 = edge1_p.data.target;
12397 var srcEdges1 = src1._private.edges; // look at edges connected to the src node of this edge
12398
12399 for (var j = 0; j < srcEdges1.length; j++) {
12400 var edge2 = srcEdges1[j];
12401 var edge2data = edge2._private.data;
12402 var tgtid2 = edge2data.target;
12403 var srcid2 = edge2data.source;
12404 var codirected = tgtid2 === tgtid1 && srcid2 === srcid1;
12405 var oppdirected = srcid1 === tgtid2 && tgtid1 === srcid2;
12406
12407 if (p.codirected && codirected || !p.codirected && (codirected || oppdirected)) {
12408 elements.push(edge2);
12409 }
12410 }
12411 }
12412
12413 return this.spawn(elements, true).filter(selector);
12414 };
12415} // Misc functions
12416/////////////////
12417
12418
12419extend(elesfn$t, {
12420 components: function components(root) {
12421 var self = this;
12422 var cy = self.cy();
12423 var visited = cy.collection();
12424 var unvisited = root == null ? self.nodes() : root.nodes();
12425 var components = [];
12426
12427 if (root != null && unvisited.empty()) {
12428 // root may contain only edges
12429 unvisited = root.sources(); // doesn't matter which node to use (undirected), so just use the source sides
12430 }
12431
12432 var visitInComponent = function visitInComponent(node, component) {
12433 visited.merge(node);
12434 unvisited.unmerge(node);
12435 component.merge(node);
12436 };
12437
12438 if (unvisited.empty()) {
12439 return self.spawn();
12440 }
12441
12442 var _loop = function _loop() {
12443 // each iteration yields a component
12444 var cmpt = cy.collection();
12445 components.push(cmpt);
12446 var root = unvisited[0];
12447 visitInComponent(root, cmpt);
12448 self.bfs({
12449 directed: false,
12450 roots: root,
12451 visit: function visit(v) {
12452 return visitInComponent(v, cmpt);
12453 }
12454 });
12455 cmpt.forEach(function (node) {
12456 node.connectedEdges().forEach(function (e) {
12457 // connectedEdges() usually cached
12458 if (self.has(e) && cmpt.has(e.source()) && cmpt.has(e.target())) {
12459 // has() is cheap
12460 cmpt.merge(e); // forEach() only considers nodes -- sets N at call time
12461 }
12462 });
12463 });
12464 };
12465
12466 do {
12467 _loop();
12468 } while (unvisited.length > 0);
12469
12470 return components;
12471 },
12472 component: function component() {
12473 var ele = this[0];
12474 return ele.cy().mutableElements().components(ele)[0];
12475 }
12476});
12477elesfn$t.componentsOf = elesfn$t.components;
12478
12479var Collection = function Collection(cy, elements) {
12480 var unique = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
12481
12482 if (cy === undefined) {
12483 error('A collection must have a reference to the core');
12484 return;
12485 }
12486
12487 var map = new Map$1();
12488 var createdElements = false;
12489
12490 if (!elements) {
12491 elements = [];
12492 } else if (elements.length > 0 && plainObject(elements[0]) && !element(elements[0])) {
12493 createdElements = true; // make elements from json and restore all at once later
12494
12495 var eles = [];
12496 var elesIds = new Set$1();
12497
12498 for (var i = 0, l = elements.length; i < l; i++) {
12499 var json = elements[i];
12500
12501 if (json.data == null) {
12502 json.data = {};
12503 }
12504
12505 var _data = json.data; // make sure newly created elements have valid ids
12506
12507 if (_data.id == null) {
12508 _data.id = uuid();
12509 } else if (cy.hasElementWithId(_data.id) || elesIds.has(_data.id)) {
12510 continue; // can't create element if prior id already exists
12511 }
12512
12513 var ele = new Element(cy, json, false);
12514 eles.push(ele);
12515 elesIds.add(_data.id);
12516 }
12517
12518 elements = eles;
12519 }
12520
12521 this.length = 0;
12522
12523 for (var _i = 0, _l = elements.length; _i < _l; _i++) {
12524 var element$1 = elements[_i][0]; // [0] in case elements is an array of collections, rather than array of elements
12525
12526 if (element$1 == null) {
12527 continue;
12528 }
12529
12530 var id = element$1._private.data.id;
12531
12532 if (!unique || !map.has(id)) {
12533 if (unique) {
12534 map.set(id, {
12535 index: this.length,
12536 ele: element$1
12537 });
12538 }
12539
12540 this[this.length] = element$1;
12541 this.length++;
12542 }
12543 }
12544
12545 this._private = {
12546 eles: this,
12547 cy: cy,
12548
12549 get map() {
12550 if (this.lazyMap == null) {
12551 this.rebuildMap();
12552 }
12553
12554 return this.lazyMap;
12555 },
12556
12557 set map(m) {
12558 this.lazyMap = m;
12559 },
12560
12561 rebuildMap: function rebuildMap() {
12562 var m = this.lazyMap = new Map$1();
12563 var eles = this.eles;
12564
12565 for (var _i2 = 0; _i2 < eles.length; _i2++) {
12566 var _ele = eles[_i2];
12567 m.set(_ele.id(), {
12568 index: _i2,
12569 ele: _ele
12570 });
12571 }
12572 }
12573 };
12574
12575 if (unique) {
12576 this._private.map = map;
12577 } // restore the elements if we created them from json
12578
12579
12580 if (createdElements) {
12581 this.restore();
12582 }
12583}; // Functions
12584////////////////////////////////////////////////////////////////////////////////////////////////////
12585// keep the prototypes in sync (an element has the same functions as a collection)
12586// and use elefn and elesfn as shorthands to the prototypes
12587
12588
12589var elesfn$u = Element.prototype = Collection.prototype = Object.create(Array.prototype);
12590
12591elesfn$u.instanceString = function () {
12592 return 'collection';
12593};
12594
12595elesfn$u.spawn = function (eles, unique) {
12596 return new Collection(this.cy(), eles, unique);
12597};
12598
12599elesfn$u.spawnSelf = function () {
12600 return this.spawn(this);
12601};
12602
12603elesfn$u.cy = function () {
12604 return this._private.cy;
12605};
12606
12607elesfn$u.renderer = function () {
12608 return this._private.cy.renderer();
12609};
12610
12611elesfn$u.element = function () {
12612 return this[0];
12613};
12614
12615elesfn$u.collection = function () {
12616 if (collection(this)) {
12617 return this;
12618 } else {
12619 // an element
12620 return new Collection(this._private.cy, [this]);
12621 }
12622};
12623
12624elesfn$u.unique = function () {
12625 return new Collection(this._private.cy, this, true);
12626};
12627
12628elesfn$u.hasElementWithId = function (id) {
12629 id = '' + id; // id must be string
12630
12631 return this._private.map.has(id);
12632};
12633
12634elesfn$u.getElementById = function (id) {
12635 id = '' + id; // id must be string
12636
12637 var cy = this._private.cy;
12638
12639 var entry = this._private.map.get(id);
12640
12641 return entry ? entry.ele : new Collection(cy); // get ele or empty collection
12642};
12643
12644elesfn$u.$id = elesfn$u.getElementById;
12645
12646elesfn$u.poolIndex = function () {
12647 var cy = this._private.cy;
12648 var eles = cy._private.elements;
12649 var id = this[0]._private.data.id;
12650 return eles._private.map.get(id).index;
12651};
12652
12653elesfn$u.indexOf = function (ele) {
12654 var id = ele[0]._private.data.id;
12655 return this._private.map.get(id).index;
12656};
12657
12658elesfn$u.indexOfId = function (id) {
12659 id = '' + id; // id must be string
12660
12661 return this._private.map.get(id).index;
12662};
12663
12664elesfn$u.json = function (obj) {
12665 var ele = this.element();
12666 var cy = this.cy();
12667
12668 if (ele == null && obj) {
12669 return this;
12670 } // can't set to no eles
12671
12672
12673 if (ele == null) {
12674 return undefined;
12675 } // can't get from no eles
12676
12677
12678 var p = ele._private;
12679
12680 if (plainObject(obj)) {
12681 // set
12682 cy.startBatch();
12683
12684 if (obj.data) {
12685 ele.data(obj.data);
12686 var _data2 = p.data;
12687
12688 if (ele.isEdge()) {
12689 // source and target are immutable via data()
12690 var move = false;
12691 var spec = {};
12692 var src = obj.data.source;
12693 var tgt = obj.data.target;
12694
12695 if (src != null && src != _data2.source) {
12696 spec.source = '' + src; // id must be string
12697
12698 move = true;
12699 }
12700
12701 if (tgt != null && tgt != _data2.target) {
12702 spec.target = '' + tgt; // id must be string
12703
12704 move = true;
12705 }
12706
12707 if (move) {
12708 ele = ele.move(spec);
12709 }
12710 } else {
12711 // parent is immutable via data()
12712 var newParentValSpecd = 'parent' in obj.data;
12713 var parent = obj.data.parent;
12714
12715 if (newParentValSpecd && (parent != null || _data2.parent != null) && parent != _data2.parent) {
12716 if (parent === undefined) {
12717 // can't set undefined imperatively, so use null
12718 parent = null;
12719 }
12720
12721 if (parent != null) {
12722 parent = '' + parent; // id must be string
12723 }
12724
12725 ele = ele.move({
12726 parent: parent
12727 });
12728 }
12729 }
12730 }
12731
12732 if (obj.position) {
12733 ele.position(obj.position);
12734 } // ignore group -- immutable
12735
12736
12737 var checkSwitch = function checkSwitch(k, trueFnName, falseFnName) {
12738 var obj_k = obj[k];
12739
12740 if (obj_k != null && obj_k !== p[k]) {
12741 if (obj_k) {
12742 ele[trueFnName]();
12743 } else {
12744 ele[falseFnName]();
12745 }
12746 }
12747 };
12748
12749 checkSwitch('removed', 'remove', 'restore');
12750 checkSwitch('selected', 'select', 'unselect');
12751 checkSwitch('selectable', 'selectify', 'unselectify');
12752 checkSwitch('locked', 'lock', 'unlock');
12753 checkSwitch('grabbable', 'grabify', 'ungrabify');
12754 checkSwitch('pannable', 'panify', 'unpanify');
12755
12756 if (obj.classes != null) {
12757 ele.classes(obj.classes);
12758 }
12759
12760 cy.endBatch();
12761 return this;
12762 } else if (obj === undefined) {
12763 // get
12764 var json = {
12765 data: copy(p.data),
12766 position: copy(p.position),
12767 group: p.group,
12768 removed: p.removed,
12769 selected: p.selected,
12770 selectable: p.selectable,
12771 locked: p.locked,
12772 grabbable: p.grabbable,
12773 pannable: p.pannable,
12774 classes: null
12775 };
12776 json.classes = '';
12777 var i = 0;
12778 p.classes.forEach(function (cls) {
12779 return json.classes += i++ === 0 ? cls : ' ' + cls;
12780 });
12781 return json;
12782 }
12783};
12784
12785elesfn$u.jsons = function () {
12786 var jsons = [];
12787
12788 for (var i = 0; i < this.length; i++) {
12789 var ele = this[i];
12790 var json = ele.json();
12791 jsons.push(json);
12792 }
12793
12794 return jsons;
12795};
12796
12797elesfn$u.clone = function () {
12798 var cy = this.cy();
12799 var elesArr = [];
12800
12801 for (var i = 0; i < this.length; i++) {
12802 var ele = this[i];
12803 var json = ele.json();
12804 var clone = new Element(cy, json, false); // NB no restore
12805
12806 elesArr.push(clone);
12807 }
12808
12809 return new Collection(cy, elesArr);
12810};
12811
12812elesfn$u.copy = elesfn$u.clone;
12813
12814elesfn$u.restore = function () {
12815 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
12816 var addToPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
12817 var self = this;
12818 var cy = self.cy();
12819 var cy_p = cy._private; // create arrays of nodes and edges, since we need to
12820 // restore the nodes first
12821
12822 var nodes = [];
12823 var edges = [];
12824 var elements;
12825
12826 for (var _i3 = 0, l = self.length; _i3 < l; _i3++) {
12827 var ele = self[_i3];
12828
12829 if (addToPool && !ele.removed()) {
12830 // don't need to handle this ele
12831 continue;
12832 } // keep nodes first in the array and edges after
12833
12834
12835 if (ele.isNode()) {
12836 // put to front of array if node
12837 nodes.push(ele);
12838 } else {
12839 // put to end of array if edge
12840 edges.push(ele);
12841 }
12842 }
12843
12844 elements = nodes.concat(edges);
12845 var i;
12846
12847 var removeFromElements = function removeFromElements() {
12848 elements.splice(i, 1);
12849 i--;
12850 }; // now, restore each element
12851
12852
12853 for (i = 0; i < elements.length; i++) {
12854 var _ele2 = elements[i];
12855 var _private = _ele2._private;
12856 var _data3 = _private.data; // the traversal cache should start fresh when ele is added
12857
12858 _ele2.clearTraversalCache(); // set id and validate
12859
12860
12861 if (!addToPool && !_private.removed) ; else if (_data3.id === undefined) {
12862 _data3.id = uuid();
12863 } else if (number(_data3.id)) {
12864 _data3.id = '' + _data3.id; // now it's a string
12865 } else if (emptyString(_data3.id) || !string(_data3.id)) {
12866 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
12867
12868 removeFromElements();
12869 continue;
12870 } else if (cy.hasElementWithId(_data3.id)) {
12871 error('Can not create second element with ID `' + _data3.id + '`'); // can't create element if one already has that id
12872
12873 removeFromElements();
12874 continue;
12875 }
12876
12877 var id = _data3.id; // id is finalised, now let's keep a ref
12878
12879 if (_ele2.isNode()) {
12880 // extra checks for nodes
12881 var pos = _private.position; // make sure the nodes have a defined position
12882
12883 if (pos.x == null) {
12884 pos.x = 0;
12885 }
12886
12887 if (pos.y == null) {
12888 pos.y = 0;
12889 }
12890 }
12891
12892 if (_ele2.isEdge()) {
12893 // extra checks for edges
12894 var edge = _ele2;
12895 var fields = ['source', 'target'];
12896 var fieldsLength = fields.length;
12897 var badSourceOrTarget = false;
12898
12899 for (var j = 0; j < fieldsLength; j++) {
12900 var field = fields[j];
12901 var val = _data3[field];
12902
12903 if (number(val)) {
12904 val = _data3[field] = '' + _data3[field]; // now string
12905 }
12906
12907 if (val == null || val === '') {
12908 // can't create if source or target is not defined properly
12909 error('Can not create edge `' + id + '` with unspecified ' + field);
12910 badSourceOrTarget = true;
12911 } else if (!cy.hasElementWithId(val)) {
12912 // can't create edge if one of its nodes doesn't exist
12913 error('Can not create edge `' + id + '` with nonexistant ' + field + ' `' + val + '`');
12914 badSourceOrTarget = true;
12915 }
12916 }
12917
12918 if (badSourceOrTarget) {
12919 removeFromElements();
12920 continue;
12921 } // can't create this
12922
12923
12924 var src = cy.getElementById(_data3.source);
12925 var tgt = cy.getElementById(_data3.target); // only one edge in node if loop
12926
12927 if (src.same(tgt)) {
12928 src._private.edges.push(edge);
12929 } else {
12930 src._private.edges.push(edge);
12931
12932 tgt._private.edges.push(edge);
12933 }
12934
12935 edge._private.source = src;
12936 edge._private.target = tgt;
12937 } // if is edge
12938 // create mock ids / indexes maps for element so it can be used like collections
12939
12940
12941 _private.map = new Map$1();
12942
12943 _private.map.set(id, {
12944 ele: _ele2,
12945 index: 0
12946 });
12947
12948 _private.removed = false;
12949
12950 if (addToPool) {
12951 cy.addToPool(_ele2);
12952 }
12953 } // for each element
12954 // do compound node sanity checks
12955
12956
12957 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
12958 // each node
12959 var node = nodes[_i4];
12960 var _data4 = node._private.data;
12961
12962 if (number(_data4.parent)) {
12963 // then automake string
12964 _data4.parent = '' + _data4.parent;
12965 }
12966
12967 var parentId = _data4.parent;
12968 var specifiedParent = parentId != null;
12969
12970 if (specifiedParent) {
12971 var parent = cy.getElementById(parentId);
12972
12973 if (parent.empty()) {
12974 // non-existant parent; just remove it
12975 _data4.parent = undefined;
12976 } else {
12977 var selfAsParent = false;
12978 var ancestor = parent;
12979
12980 while (!ancestor.empty()) {
12981 if (node.same(ancestor)) {
12982 // mark self as parent and remove from data
12983 selfAsParent = true;
12984 _data4.parent = undefined; // remove parent reference
12985 // exit or we loop forever
12986
12987 break;
12988 }
12989
12990 ancestor = ancestor.parent();
12991 }
12992
12993 if (!selfAsParent) {
12994 // connect with children
12995 parent[0]._private.children.push(node);
12996
12997 node._private.parent = parent[0]; // let the core know we have a compound graph
12998
12999 cy_p.hasCompoundNodes = true;
13000 }
13001 } // else
13002
13003 } // if specified parent
13004
13005 } // for each node
13006
13007
13008 if (elements.length > 0) {
13009 var restored = elements.length === self.length ? self : new Collection(cy, elements);
13010
13011 for (var _i5 = 0; _i5 < restored.length; _i5++) {
13012 var _ele3 = restored[_i5];
13013
13014 if (_ele3.isNode()) {
13015 continue;
13016 } // adding an edge invalidates the traversal caches for the parallel edges
13017
13018
13019 _ele3.parallelEdges().clearTraversalCache(); // adding an edge invalidates the traversal cache for the connected nodes
13020
13021
13022 _ele3.source().clearTraversalCache();
13023
13024 _ele3.target().clearTraversalCache();
13025 }
13026
13027 var toUpdateStyle;
13028
13029 if (cy_p.hasCompoundNodes) {
13030 toUpdateStyle = cy.collection().merge(restored).merge(restored.connectedNodes()).merge(restored.parent());
13031 } else {
13032 toUpdateStyle = restored;
13033 }
13034
13035 toUpdateStyle.dirtyCompoundBoundsCache().dirtyBoundingBoxCache().updateStyle(notifyRenderer);
13036
13037 if (notifyRenderer) {
13038 restored.emitAndNotify('add');
13039 } else if (addToPool) {
13040 restored.emit('add');
13041 }
13042 }
13043
13044 return self; // chainability
13045};
13046
13047elesfn$u.removed = function () {
13048 var ele = this[0];
13049 return ele && ele._private.removed;
13050};
13051
13052elesfn$u.inside = function () {
13053 var ele = this[0];
13054 return ele && !ele._private.removed;
13055};
13056
13057elesfn$u.remove = function () {
13058 var notifyRenderer = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
13059 var removeFromPool = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
13060 var self = this;
13061 var elesToRemove = [];
13062 var elesToRemoveIds = {};
13063 var cy = self._private.cy; // add connected edges
13064
13065 function addConnectedEdges(node) {
13066 var edges = node._private.edges;
13067
13068 for (var i = 0; i < edges.length; i++) {
13069 add(edges[i]);
13070 }
13071 } // add descendant nodes
13072
13073
13074 function addChildren(node) {
13075 var children = node._private.children;
13076
13077 for (var i = 0; i < children.length; i++) {
13078 add(children[i]);
13079 }
13080 }
13081
13082 function add(ele) {
13083 var alreadyAdded = elesToRemoveIds[ele.id()];
13084
13085 if (removeFromPool && ele.removed() || alreadyAdded) {
13086 return;
13087 } else {
13088 elesToRemoveIds[ele.id()] = true;
13089 }
13090
13091 if (ele.isNode()) {
13092 elesToRemove.push(ele); // nodes are removed last
13093
13094 addConnectedEdges(ele);
13095 addChildren(ele);
13096 } else {
13097 elesToRemove.unshift(ele); // edges are removed first
13098 }
13099 } // make the list of elements to remove
13100 // (may be removing more than specified due to connected edges etc)
13101
13102
13103 for (var i = 0, l = self.length; i < l; i++) {
13104 var ele = self[i];
13105 add(ele);
13106 }
13107
13108 function removeEdgeRef(node, edge) {
13109 var connectedEdges = node._private.edges;
13110 removeFromArray(connectedEdges, edge); // removing an edges invalidates the traversal cache for its nodes
13111
13112 node.clearTraversalCache();
13113 }
13114
13115 function removeParallelRef(pllEdge) {
13116 // removing an edge invalidates the traversal caches for the parallel edges
13117 pllEdge.clearTraversalCache();
13118 }
13119
13120 var alteredParents = [];
13121 alteredParents.ids = {};
13122
13123 function removeChildRef(parent, ele) {
13124 ele = ele[0];
13125 parent = parent[0];
13126 var children = parent._private.children;
13127 var pid = parent.id();
13128 removeFromArray(children, ele); // remove parent => child ref
13129
13130 ele._private.parent = null; // remove child => parent ref
13131
13132 if (!alteredParents.ids[pid]) {
13133 alteredParents.ids[pid] = true;
13134 alteredParents.push(parent);
13135 }
13136 }
13137
13138 self.dirtyCompoundBoundsCache();
13139
13140 if (removeFromPool) {
13141 cy.removeFromPool(elesToRemove); // remove from core pool
13142 }
13143
13144 for (var _i6 = 0; _i6 < elesToRemove.length; _i6++) {
13145 var _ele4 = elesToRemove[_i6];
13146
13147 if (_ele4.isEdge()) {
13148 // remove references to this edge in its connected nodes
13149 var src = _ele4.source()[0];
13150
13151 var tgt = _ele4.target()[0];
13152
13153 removeEdgeRef(src, _ele4);
13154 removeEdgeRef(tgt, _ele4);
13155
13156 var pllEdges = _ele4.parallelEdges();
13157
13158 for (var j = 0; j < pllEdges.length; j++) {
13159 var pllEdge = pllEdges[j];
13160 removeParallelRef(pllEdge);
13161
13162 if (pllEdge.isBundledBezier()) {
13163 pllEdge.dirtyBoundingBoxCache();
13164 }
13165 }
13166 } else {
13167 // remove reference to parent
13168 var parent = _ele4.parent();
13169
13170 if (parent.length !== 0) {
13171 removeChildRef(parent, _ele4);
13172 }
13173 }
13174
13175 if (removeFromPool) {
13176 // mark as removed
13177 _ele4._private.removed = true;
13178 }
13179 } // check to see if we have a compound graph or not
13180
13181
13182 var elesStillInside = cy._private.elements;
13183 cy._private.hasCompoundNodes = false;
13184
13185 for (var _i7 = 0; _i7 < elesStillInside.length; _i7++) {
13186 var _ele5 = elesStillInside[_i7];
13187
13188 if (_ele5.isParent()) {
13189 cy._private.hasCompoundNodes = true;
13190 break;
13191 }
13192 }
13193
13194 var removedElements = new Collection(this.cy(), elesToRemove);
13195
13196 if (removedElements.size() > 0) {
13197 // must manually notify since trigger won't do this automatically once removed
13198 if (notifyRenderer) {
13199 removedElements.emitAndNotify('remove');
13200 } else if (removeFromPool) {
13201 removedElements.emit('remove');
13202 }
13203 } // the parents who were modified by the removal need their style updated
13204
13205
13206 for (var _i8 = 0; _i8 < alteredParents.length; _i8++) {
13207 var _ele6 = alteredParents[_i8];
13208
13209 if (!removeFromPool || !_ele6.removed()) {
13210 _ele6.updateStyle();
13211 }
13212 }
13213
13214 return removedElements;
13215};
13216
13217elesfn$u.move = function (struct) {
13218 var cy = this._private.cy;
13219 var eles = this; // just clean up refs, caches, etc. in the same way as when removing and then restoring
13220 // (our calls to remove/restore do not remove from the graph or make events)
13221
13222 var notifyRenderer = false;
13223 var modifyPool = false;
13224
13225 var toString = function toString(id) {
13226 return id == null ? id : '' + id;
13227 }; // id must be string
13228
13229
13230 if (struct.source !== undefined || struct.target !== undefined) {
13231 var srcId = toString(struct.source);
13232 var tgtId = toString(struct.target);
13233 var srcExists = srcId != null && cy.hasElementWithId(srcId);
13234 var tgtExists = tgtId != null && cy.hasElementWithId(tgtId);
13235
13236 if (srcExists || tgtExists) {
13237 cy.batch(function () {
13238 // avoid duplicate style updates
13239 eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13240
13241 eles.emitAndNotify('moveout');
13242
13243 for (var i = 0; i < eles.length; i++) {
13244 var ele = eles[i];
13245 var _data5 = ele._private.data;
13246
13247 if (ele.isEdge()) {
13248 if (srcExists) {
13249 _data5.source = srcId;
13250 }
13251
13252 if (tgtExists) {
13253 _data5.target = tgtId;
13254 }
13255 }
13256 }
13257
13258 eles.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13259 });
13260 eles.emitAndNotify('move');
13261 }
13262 } else if (struct.parent !== undefined) {
13263 // move node to new parent
13264 var parentId = toString(struct.parent);
13265 var parentExists = parentId === null || cy.hasElementWithId(parentId);
13266
13267 if (parentExists) {
13268 var pidToAssign = parentId === null ? undefined : parentId;
13269 cy.batch(function () {
13270 // avoid duplicate style updates
13271 var updated = eles.remove(notifyRenderer, modifyPool); // clean up refs etc.
13272
13273 updated.emitAndNotify('moveout');
13274
13275 for (var i = 0; i < eles.length; i++) {
13276 var ele = eles[i];
13277 var _data6 = ele._private.data;
13278
13279 if (ele.isNode()) {
13280 _data6.parent = pidToAssign;
13281 }
13282 }
13283
13284 updated.restore(notifyRenderer, modifyPool); // make new refs, style, etc.
13285 });
13286 eles.emitAndNotify('move');
13287 }
13288 }
13289
13290 return this;
13291};
13292
13293[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) {
13294 extend(elesfn$u, props);
13295});
13296
13297var corefn = {
13298 add: function add(opts) {
13299 var elements;
13300 var cy = this; // add the elements
13301
13302 if (elementOrCollection(opts)) {
13303 var eles = opts;
13304
13305 if (eles._private.cy === cy) {
13306 // same instance => just restore
13307 elements = eles.restore();
13308 } else {
13309 // otherwise, copy from json
13310 var jsons = [];
13311
13312 for (var i = 0; i < eles.length; i++) {
13313 var ele = eles[i];
13314 jsons.push(ele.json());
13315 }
13316
13317 elements = new Collection(cy, jsons);
13318 }
13319 } // specify an array of options
13320 else if (array(opts)) {
13321 var _jsons = opts;
13322 elements = new Collection(cy, _jsons);
13323 } // specify via opts.nodes and opts.edges
13324 else if (plainObject(opts) && (array(opts.nodes) || array(opts.edges))) {
13325 var elesByGroup = opts;
13326 var _jsons2 = [];
13327 var grs = ['nodes', 'edges'];
13328
13329 for (var _i = 0, il = grs.length; _i < il; _i++) {
13330 var group = grs[_i];
13331 var elesArray = elesByGroup[group];
13332
13333 if (array(elesArray)) {
13334 for (var j = 0, jl = elesArray.length; j < jl; j++) {
13335 var json = extend({
13336 group: group
13337 }, elesArray[j]);
13338
13339 _jsons2.push(json);
13340 }
13341 }
13342 }
13343
13344 elements = new Collection(cy, _jsons2);
13345 } // specify options for one element
13346 else {
13347 var _json = opts;
13348 elements = new Element(cy, _json).collection();
13349 }
13350
13351 return elements;
13352 },
13353 remove: function remove(collection) {
13354 if (elementOrCollection(collection)) ; else if (string(collection)) {
13355 var selector = collection;
13356 collection = this.$(selector);
13357 }
13358
13359 return collection.remove();
13360 }
13361};
13362
13363/* global Float32Array */
13364
13365/*! Bezier curve function generator. Copyright Gaetan Renaudeau. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13366function generateCubicBezier(mX1, mY1, mX2, mY2) {
13367 var NEWTON_ITERATIONS = 4,
13368 NEWTON_MIN_SLOPE = 0.001,
13369 SUBDIVISION_PRECISION = 0.0000001,
13370 SUBDIVISION_MAX_ITERATIONS = 10,
13371 kSplineTableSize = 11,
13372 kSampleStepSize = 1.0 / (kSplineTableSize - 1.0),
13373 float32ArraySupported = typeof Float32Array !== 'undefined';
13374 /* Must contain four arguments. */
13375
13376 if (arguments.length !== 4) {
13377 return false;
13378 }
13379 /* Arguments must be numbers. */
13380
13381
13382 for (var i = 0; i < 4; ++i) {
13383 if (typeof arguments[i] !== "number" || isNaN(arguments[i]) || !isFinite(arguments[i])) {
13384 return false;
13385 }
13386 }
13387 /* X values must be in the [0, 1] range. */
13388
13389
13390 mX1 = Math.min(mX1, 1);
13391 mX2 = Math.min(mX2, 1);
13392 mX1 = Math.max(mX1, 0);
13393 mX2 = Math.max(mX2, 0);
13394 var mSampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize);
13395
13396 function A(aA1, aA2) {
13397 return 1.0 - 3.0 * aA2 + 3.0 * aA1;
13398 }
13399
13400 function B(aA1, aA2) {
13401 return 3.0 * aA2 - 6.0 * aA1;
13402 }
13403
13404 function C(aA1) {
13405 return 3.0 * aA1;
13406 }
13407
13408 function calcBezier(aT, aA1, aA2) {
13409 return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT;
13410 }
13411
13412 function getSlope(aT, aA1, aA2) {
13413 return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1);
13414 }
13415
13416 function newtonRaphsonIterate(aX, aGuessT) {
13417 for (var _i = 0; _i < NEWTON_ITERATIONS; ++_i) {
13418 var currentSlope = getSlope(aGuessT, mX1, mX2);
13419
13420 if (currentSlope === 0.0) {
13421 return aGuessT;
13422 }
13423
13424 var currentX = calcBezier(aGuessT, mX1, mX2) - aX;
13425 aGuessT -= currentX / currentSlope;
13426 }
13427
13428 return aGuessT;
13429 }
13430
13431 function calcSampleValues() {
13432 for (var _i2 = 0; _i2 < kSplineTableSize; ++_i2) {
13433 mSampleValues[_i2] = calcBezier(_i2 * kSampleStepSize, mX1, mX2);
13434 }
13435 }
13436
13437 function binarySubdivide(aX, aA, aB) {
13438 var currentX,
13439 currentT,
13440 i = 0;
13441
13442 do {
13443 currentT = aA + (aB - aA) / 2.0;
13444 currentX = calcBezier(currentT, mX1, mX2) - aX;
13445
13446 if (currentX > 0.0) {
13447 aB = currentT;
13448 } else {
13449 aA = currentT;
13450 }
13451 } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS);
13452
13453 return currentT;
13454 }
13455
13456 function getTForX(aX) {
13457 var intervalStart = 0.0,
13458 currentSample = 1,
13459 lastSample = kSplineTableSize - 1;
13460
13461 for (; currentSample !== lastSample && mSampleValues[currentSample] <= aX; ++currentSample) {
13462 intervalStart += kSampleStepSize;
13463 }
13464
13465 --currentSample;
13466 var dist = (aX - mSampleValues[currentSample]) / (mSampleValues[currentSample + 1] - mSampleValues[currentSample]),
13467 guessForT = intervalStart + dist * kSampleStepSize,
13468 initialSlope = getSlope(guessForT, mX1, mX2);
13469
13470 if (initialSlope >= NEWTON_MIN_SLOPE) {
13471 return newtonRaphsonIterate(aX, guessForT);
13472 } else if (initialSlope === 0.0) {
13473 return guessForT;
13474 } else {
13475 return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize);
13476 }
13477 }
13478
13479 var _precomputed = false;
13480
13481 function precompute() {
13482 _precomputed = true;
13483
13484 if (mX1 !== mY1 || mX2 !== mY2) {
13485 calcSampleValues();
13486 }
13487 }
13488
13489 var f = function f(aX) {
13490 if (!_precomputed) {
13491 precompute();
13492 }
13493
13494 if (mX1 === mY1 && mX2 === mY2) {
13495 return aX;
13496 }
13497
13498 if (aX === 0) {
13499 return 0;
13500 }
13501
13502 if (aX === 1) {
13503 return 1;
13504 }
13505
13506 return calcBezier(getTForX(aX), mY1, mY2);
13507 };
13508
13509 f.getControlPoints = function () {
13510 return [{
13511 x: mX1,
13512 y: mY1
13513 }, {
13514 x: mX2,
13515 y: mY2
13516 }];
13517 };
13518
13519 var str = "generateBezier(" + [mX1, mY1, mX2, mY2] + ")";
13520
13521 f.toString = function () {
13522 return str;
13523 };
13524
13525 return f;
13526}
13527
13528/*! Runge-Kutta spring physics function generator. Adapted from Framer.js, copyright Koen Bok. MIT License: http://en.wikipedia.org/wiki/MIT_License */
13529
13530/* 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
13531 then adjusts the time delta -- using the relation between actual time and duration -- to calculate the path for the duration-constrained animation. */
13532var generateSpringRK4 = function () {
13533 function springAccelerationForState(state) {
13534 return -state.tension * state.x - state.friction * state.v;
13535 }
13536
13537 function springEvaluateStateWithDerivative(initialState, dt, derivative) {
13538 var state = {
13539 x: initialState.x + derivative.dx * dt,
13540 v: initialState.v + derivative.dv * dt,
13541 tension: initialState.tension,
13542 friction: initialState.friction
13543 };
13544 return {
13545 dx: state.v,
13546 dv: springAccelerationForState(state)
13547 };
13548 }
13549
13550 function springIntegrateState(state, dt) {
13551 var a = {
13552 dx: state.v,
13553 dv: springAccelerationForState(state)
13554 },
13555 b = springEvaluateStateWithDerivative(state, dt * 0.5, a),
13556 c = springEvaluateStateWithDerivative(state, dt * 0.5, b),
13557 d = springEvaluateStateWithDerivative(state, dt, c),
13558 dxdt = 1.0 / 6.0 * (a.dx + 2.0 * (b.dx + c.dx) + d.dx),
13559 dvdt = 1.0 / 6.0 * (a.dv + 2.0 * (b.dv + c.dv) + d.dv);
13560 state.x = state.x + dxdt * dt;
13561 state.v = state.v + dvdt * dt;
13562 return state;
13563 }
13564
13565 return function springRK4Factory(tension, friction, duration) {
13566 var initState = {
13567 x: -1,
13568 v: 0,
13569 tension: null,
13570 friction: null
13571 },
13572 path = [0],
13573 time_lapsed = 0,
13574 tolerance = 1 / 10000,
13575 DT = 16 / 1000,
13576 have_duration,
13577 dt,
13578 last_state;
13579 tension = parseFloat(tension) || 500;
13580 friction = parseFloat(friction) || 20;
13581 duration = duration || null;
13582 initState.tension = tension;
13583 initState.friction = friction;
13584 have_duration = duration !== null;
13585 /* Calculate the actual time it takes for this animation to complete with the provided conditions. */
13586
13587 if (have_duration) {
13588 /* Run the simulation without a duration. */
13589 time_lapsed = springRK4Factory(tension, friction);
13590 /* Compute the adjusted time delta. */
13591
13592 dt = time_lapsed / duration * DT;
13593 } else {
13594 dt = DT;
13595 }
13596
13597 for (;;) {
13598 /* Next/step function .*/
13599 last_state = springIntegrateState(last_state || initState, dt);
13600 /* Store the position. */
13601
13602 path.push(1 + last_state.x);
13603 time_lapsed += 16;
13604 /* If the change threshold is reached, break. */
13605
13606 if (!(Math.abs(last_state.x) > tolerance && Math.abs(last_state.v) > tolerance)) {
13607 break;
13608 }
13609 }
13610 /* If duration is not defined, return the actual time required for completing this animation. Otherwise, return a closure that holds the
13611 computed path and returns a snapshot of the position according to a given percentComplete. */
13612
13613
13614 return !have_duration ? time_lapsed : function (percentComplete) {
13615 return path[percentComplete * (path.length - 1) | 0];
13616 };
13617 };
13618}();
13619
13620var cubicBezier = function cubicBezier(t1, p1, t2, p2) {
13621 var bezier = generateCubicBezier(t1, p1, t2, p2);
13622 return function (start, end, percent) {
13623 return start + (end - start) * bezier(percent);
13624 };
13625};
13626
13627var easings = {
13628 'linear': function linear(start, end, percent) {
13629 return start + (end - start) * percent;
13630 },
13631 // default easings
13632 'ease': cubicBezier(0.25, 0.1, 0.25, 1),
13633 'ease-in': cubicBezier(0.42, 0, 1, 1),
13634 'ease-out': cubicBezier(0, 0, 0.58, 1),
13635 'ease-in-out': cubicBezier(0.42, 0, 0.58, 1),
13636 // sine
13637 'ease-in-sine': cubicBezier(0.47, 0, 0.745, 0.715),
13638 'ease-out-sine': cubicBezier(0.39, 0.575, 0.565, 1),
13639 'ease-in-out-sine': cubicBezier(0.445, 0.05, 0.55, 0.95),
13640 // quad
13641 'ease-in-quad': cubicBezier(0.55, 0.085, 0.68, 0.53),
13642 'ease-out-quad': cubicBezier(0.25, 0.46, 0.45, 0.94),
13643 'ease-in-out-quad': cubicBezier(0.455, 0.03, 0.515, 0.955),
13644 // cubic
13645 'ease-in-cubic': cubicBezier(0.55, 0.055, 0.675, 0.19),
13646 'ease-out-cubic': cubicBezier(0.215, 0.61, 0.355, 1),
13647 'ease-in-out-cubic': cubicBezier(0.645, 0.045, 0.355, 1),
13648 // quart
13649 'ease-in-quart': cubicBezier(0.895, 0.03, 0.685, 0.22),
13650 'ease-out-quart': cubicBezier(0.165, 0.84, 0.44, 1),
13651 'ease-in-out-quart': cubicBezier(0.77, 0, 0.175, 1),
13652 // quint
13653 'ease-in-quint': cubicBezier(0.755, 0.05, 0.855, 0.06),
13654 'ease-out-quint': cubicBezier(0.23, 1, 0.32, 1),
13655 'ease-in-out-quint': cubicBezier(0.86, 0, 0.07, 1),
13656 // expo
13657 'ease-in-expo': cubicBezier(0.95, 0.05, 0.795, 0.035),
13658 'ease-out-expo': cubicBezier(0.19, 1, 0.22, 1),
13659 'ease-in-out-expo': cubicBezier(1, 0, 0, 1),
13660 // circ
13661 'ease-in-circ': cubicBezier(0.6, 0.04, 0.98, 0.335),
13662 'ease-out-circ': cubicBezier(0.075, 0.82, 0.165, 1),
13663 'ease-in-out-circ': cubicBezier(0.785, 0.135, 0.15, 0.86),
13664 // user param easings...
13665 'spring': function spring(tension, friction, duration) {
13666 if (duration === 0) {
13667 // can't get a spring w/ duration 0
13668 return easings.linear; // duration 0 => jump to end so impl doesn't matter
13669 }
13670
13671 var spring = generateSpringRK4(tension, friction, duration);
13672 return function (start, end, percent) {
13673 return start + (end - start) * spring(percent);
13674 };
13675 },
13676 'cubic-bezier': cubicBezier
13677};
13678
13679function getEasedValue(type, start, end, percent, easingFn) {
13680 if (percent === 1) {
13681 return end;
13682 }
13683
13684 if (start === end) {
13685 return end;
13686 }
13687
13688 var val = easingFn(start, end, percent);
13689
13690 if (type == null) {
13691 return val;
13692 }
13693
13694 if (type.roundValue || type.color) {
13695 val = Math.round(val);
13696 }
13697
13698 if (type.min !== undefined) {
13699 val = Math.max(val, type.min);
13700 }
13701
13702 if (type.max !== undefined) {
13703 val = Math.min(val, type.max);
13704 }
13705
13706 return val;
13707}
13708
13709function getValue(prop, spec) {
13710 if (prop.pfValue != null || prop.value != null) {
13711 if (prop.pfValue != null && (spec == null || spec.type.units !== '%')) {
13712 return prop.pfValue;
13713 } else {
13714 return prop.value;
13715 }
13716 } else {
13717 return prop;
13718 }
13719}
13720
13721function ease(startProp, endProp, percent, easingFn, propSpec) {
13722 var type = propSpec != null ? propSpec.type : null;
13723
13724 if (percent < 0) {
13725 percent = 0;
13726 } else if (percent > 1) {
13727 percent = 1;
13728 }
13729
13730 var start = getValue(startProp, propSpec);
13731 var end = getValue(endProp, propSpec);
13732
13733 if (number(start) && number(end)) {
13734 return getEasedValue(type, start, end, percent, easingFn);
13735 } else if (array(start) && array(end)) {
13736 var easedArr = [];
13737
13738 for (var i = 0; i < end.length; i++) {
13739 var si = start[i];
13740 var ei = end[i];
13741
13742 if (si != null && ei != null) {
13743 var val = getEasedValue(type, si, ei, percent, easingFn);
13744 easedArr.push(val);
13745 } else {
13746 easedArr.push(ei);
13747 }
13748 }
13749
13750 return easedArr;
13751 }
13752
13753 return undefined;
13754}
13755
13756function step(self, ani, now, isCore) {
13757 var isEles = !isCore;
13758 var _p = self._private;
13759 var ani_p = ani._private;
13760 var pEasing = ani_p.easing;
13761 var startTime = ani_p.startTime;
13762 var cy = isCore ? self : self.cy();
13763 var style = cy.style();
13764
13765 if (!ani_p.easingImpl) {
13766 if (pEasing == null) {
13767 // use default
13768 ani_p.easingImpl = easings['linear'];
13769 } else {
13770 // then define w/ name
13771 var easingVals;
13772
13773 if (string(pEasing)) {
13774 var easingProp = style.parse('transition-timing-function', pEasing);
13775 easingVals = easingProp.value;
13776 } else {
13777 // then assume preparsed array
13778 easingVals = pEasing;
13779 }
13780
13781 var name, args;
13782
13783 if (string(easingVals)) {
13784 name = easingVals;
13785 args = [];
13786 } else {
13787 name = easingVals[1];
13788 args = easingVals.slice(2).map(function (n) {
13789 return +n;
13790 });
13791 }
13792
13793 if (args.length > 0) {
13794 // create with args
13795 if (name === 'spring') {
13796 args.push(ani_p.duration); // need duration to generate spring
13797 }
13798
13799 ani_p.easingImpl = easings[name].apply(null, args);
13800 } else {
13801 // static impl by name
13802 ani_p.easingImpl = easings[name];
13803 }
13804 }
13805 }
13806
13807 var easing = ani_p.easingImpl;
13808 var percent;
13809
13810 if (ani_p.duration === 0) {
13811 percent = 1;
13812 } else {
13813 percent = (now - startTime) / ani_p.duration;
13814 }
13815
13816 if (ani_p.applying) {
13817 percent = ani_p.progress;
13818 }
13819
13820 if (percent < 0) {
13821 percent = 0;
13822 } else if (percent > 1) {
13823 percent = 1;
13824 }
13825
13826 if (ani_p.delay == null) {
13827 // then update
13828 var startPos = ani_p.startPosition;
13829 var endPos = ani_p.position;
13830
13831 if (endPos && isEles && !self.locked()) {
13832 var newPos = {};
13833
13834 if (valid(startPos.x, endPos.x)) {
13835 newPos.x = ease(startPos.x, endPos.x, percent, easing);
13836 }
13837
13838 if (valid(startPos.y, endPos.y)) {
13839 newPos.y = ease(startPos.y, endPos.y, percent, easing);
13840 }
13841
13842 self.position(newPos);
13843 }
13844
13845 var startPan = ani_p.startPan;
13846 var endPan = ani_p.pan;
13847 var pan = _p.pan;
13848 var animatingPan = endPan != null && isCore;
13849
13850 if (animatingPan) {
13851 if (valid(startPan.x, endPan.x)) {
13852 pan.x = ease(startPan.x, endPan.x, percent, easing);
13853 }
13854
13855 if (valid(startPan.y, endPan.y)) {
13856 pan.y = ease(startPan.y, endPan.y, percent, easing);
13857 }
13858
13859 self.emit('pan');
13860 }
13861
13862 var startZoom = ani_p.startZoom;
13863 var endZoom = ani_p.zoom;
13864 var animatingZoom = endZoom != null && isCore;
13865
13866 if (animatingZoom) {
13867 if (valid(startZoom, endZoom)) {
13868 _p.zoom = bound(_p.minZoom, ease(startZoom, endZoom, percent, easing), _p.maxZoom);
13869 }
13870
13871 self.emit('zoom');
13872 }
13873
13874 if (animatingPan || animatingZoom) {
13875 self.emit('viewport');
13876 }
13877
13878 var props = ani_p.style;
13879
13880 if (props && props.length > 0 && isEles) {
13881 for (var i = 0; i < props.length; i++) {
13882 var prop = props[i];
13883 var _name = prop.name;
13884 var end = prop;
13885 var start = ani_p.startStyle[_name];
13886 var propSpec = style.properties[start.name];
13887 var easedVal = ease(start, end, percent, easing, propSpec);
13888 style.overrideBypass(self, _name, easedVal);
13889 } // for props
13890
13891
13892 self.emit('style');
13893 } // if
13894
13895 }
13896
13897 ani_p.progress = percent;
13898 return percent;
13899}
13900
13901function valid(start, end) {
13902 if (start == null || end == null) {
13903 return false;
13904 }
13905
13906 if (number(start) && number(end)) {
13907 return true;
13908 } else if (start && end) {
13909 return true;
13910 }
13911
13912 return false;
13913}
13914
13915function startAnimation(self, ani, now, isCore) {
13916 var ani_p = ani._private;
13917 ani_p.started = true;
13918 ani_p.startTime = now - ani_p.progress * ani_p.duration;
13919}
13920
13921function stepAll(now, cy) {
13922 var eles = cy._private.aniEles;
13923 var doneEles = [];
13924
13925 function stepOne(ele, isCore) {
13926 var _p = ele._private;
13927 var current = _p.animation.current;
13928 var queue = _p.animation.queue;
13929 var ranAnis = false; // if nothing currently animating, get something from the queue
13930
13931 if (current.length === 0) {
13932 var next = queue.shift();
13933
13934 if (next) {
13935 current.push(next);
13936 }
13937 }
13938
13939 var callbacks = function callbacks(_callbacks) {
13940 for (var j = _callbacks.length - 1; j >= 0; j--) {
13941 var cb = _callbacks[j];
13942 cb();
13943 }
13944
13945 _callbacks.splice(0, _callbacks.length);
13946 }; // step and remove if done
13947
13948
13949 for (var i = current.length - 1; i >= 0; i--) {
13950 var ani = current[i];
13951 var ani_p = ani._private;
13952
13953 if (ani_p.stopped) {
13954 current.splice(i, 1);
13955 ani_p.hooked = false;
13956 ani_p.playing = false;
13957 ani_p.started = false;
13958 callbacks(ani_p.frames);
13959 continue;
13960 }
13961
13962 if (!ani_p.playing && !ani_p.applying) {
13963 continue;
13964 } // an apply() while playing shouldn't do anything
13965
13966
13967 if (ani_p.playing && ani_p.applying) {
13968 ani_p.applying = false;
13969 }
13970
13971 if (!ani_p.started) {
13972 startAnimation(ele, ani, now);
13973 }
13974
13975 step(ele, ani, now, isCore);
13976
13977 if (ani_p.applying) {
13978 ani_p.applying = false;
13979 }
13980
13981 callbacks(ani_p.frames);
13982
13983 if (ani_p.step != null) {
13984 ani_p.step(now);
13985 }
13986
13987 if (ani.completed()) {
13988 current.splice(i, 1);
13989 ani_p.hooked = false;
13990 ani_p.playing = false;
13991 ani_p.started = false;
13992 callbacks(ani_p.completes);
13993 }
13994
13995 ranAnis = true;
13996 }
13997
13998 if (!isCore && current.length === 0 && queue.length === 0) {
13999 doneEles.push(ele);
14000 }
14001
14002 return ranAnis;
14003 } // stepElement
14004 // handle all eles
14005
14006
14007 var ranEleAni = false;
14008
14009 for (var e = 0; e < eles.length; e++) {
14010 var ele = eles[e];
14011 var handledThisEle = stepOne(ele);
14012 ranEleAni = ranEleAni || handledThisEle;
14013 } // each element
14014
14015
14016 var ranCoreAni = stepOne(cy, true); // notify renderer
14017
14018 if (ranEleAni || ranCoreAni) {
14019 if (eles.length > 0) {
14020 cy.notify('draw', eles);
14021 } else {
14022 cy.notify('draw');
14023 }
14024 } // remove elements from list of currently animating if its queues are empty
14025
14026
14027 eles.unmerge(doneEles);
14028 cy.emit('step');
14029} // stepAll
14030
14031var corefn$1 = {
14032 // pull in animation functions
14033 animate: define$3.animate(),
14034 animation: define$3.animation(),
14035 animated: define$3.animated(),
14036 clearQueue: define$3.clearQueue(),
14037 delay: define$3.delay(),
14038 delayAnimation: define$3.delayAnimation(),
14039 stop: define$3.stop(),
14040 addToAnimationPool: function addToAnimationPool(eles) {
14041 var cy = this;
14042
14043 if (!cy.styleEnabled()) {
14044 return;
14045 } // save cycles when no style used
14046
14047
14048 cy._private.aniEles.merge(eles);
14049 },
14050 stopAnimationLoop: function stopAnimationLoop() {
14051 this._private.animationsRunning = false;
14052 },
14053 startAnimationLoop: function startAnimationLoop() {
14054 var cy = this;
14055 cy._private.animationsRunning = true;
14056
14057 if (!cy.styleEnabled()) {
14058 return;
14059 } // save cycles when no style used
14060 // NB the animation loop will exec in headless environments if style enabled
14061 // and explicit cy.destroy() is necessary to stop the loop
14062
14063
14064 function headlessStep() {
14065 if (!cy._private.animationsRunning) {
14066 return;
14067 }
14068
14069 requestAnimationFrame(function animationStep(now) {
14070 stepAll(now, cy);
14071 headlessStep();
14072 });
14073 }
14074
14075 var renderer = cy.renderer();
14076
14077 if (renderer && renderer.beforeRender) {
14078 // let the renderer schedule animations
14079 renderer.beforeRender(function rendererAnimationStep(willDraw, now) {
14080 stepAll(now, cy);
14081 }, renderer.beforeRenderPriorities.animations);
14082 } else {
14083 // manage the animation loop ourselves
14084 headlessStep(); // first call
14085 }
14086 }
14087};
14088
14089var emitterOptions$1 = {
14090 qualifierCompare: function qualifierCompare(selector1, selector2) {
14091 if (selector1 == null || selector2 == null) {
14092 return selector1 == null && selector2 == null;
14093 } else {
14094 return selector1.sameText(selector2);
14095 }
14096 },
14097 eventMatches: function eventMatches(cy, listener, eventObj) {
14098 var selector = listener.qualifier;
14099
14100 if (selector != null) {
14101 return cy !== eventObj.target && element(eventObj.target) && selector.matches(eventObj.target);
14102 }
14103
14104 return true;
14105 },
14106 addEventFields: function addEventFields(cy, evt) {
14107 evt.cy = cy;
14108 evt.target = cy;
14109 },
14110 callbackContext: function callbackContext(cy, listener, eventObj) {
14111 return listener.qualifier != null ? eventObj.target : cy;
14112 }
14113};
14114
14115var argSelector$1 = function argSelector(arg) {
14116 if (string(arg)) {
14117 return new Selector(arg);
14118 } else {
14119 return arg;
14120 }
14121};
14122
14123var elesfn$v = {
14124 createEmitter: function createEmitter() {
14125 var _p = this._private;
14126
14127 if (!_p.emitter) {
14128 _p.emitter = new Emitter(emitterOptions$1, this);
14129 }
14130
14131 return this;
14132 },
14133 emitter: function emitter() {
14134 return this._private.emitter;
14135 },
14136 on: function on(events, selector, callback) {
14137 this.emitter().on(events, argSelector$1(selector), callback);
14138 return this;
14139 },
14140 removeListener: function removeListener(events, selector, callback) {
14141 this.emitter().removeListener(events, argSelector$1(selector), callback);
14142 return this;
14143 },
14144 removeAllListeners: function removeAllListeners() {
14145 this.emitter().removeAllListeners();
14146 return this;
14147 },
14148 one: function one(events, selector, callback) {
14149 this.emitter().one(events, argSelector$1(selector), callback);
14150 return this;
14151 },
14152 once: function once(events, selector, callback) {
14153 this.emitter().one(events, argSelector$1(selector), callback);
14154 return this;
14155 },
14156 emit: function emit(events, extraParams) {
14157 this.emitter().emit(events, extraParams);
14158 return this;
14159 },
14160 emitAndNotify: function emitAndNotify(event, eles) {
14161 this.emit(event);
14162 this.notify(event, eles);
14163 return this;
14164 }
14165};
14166define$3.eventAliasesOn(elesfn$v);
14167
14168var corefn$2 = {
14169 png: function png(options) {
14170 var renderer = this._private.renderer;
14171 options = options || {};
14172 return renderer.png(options);
14173 },
14174 jpg: function jpg(options) {
14175 var renderer = this._private.renderer;
14176 options = options || {};
14177 options.bg = options.bg || '#fff';
14178 return renderer.jpg(options);
14179 }
14180};
14181corefn$2.jpeg = corefn$2.jpg;
14182
14183var corefn$3 = {
14184 layout: function layout(options) {
14185 var cy = this;
14186
14187 if (options == null) {
14188 error('Layout options must be specified to make a layout');
14189 return;
14190 }
14191
14192 if (options.name == null) {
14193 error('A `name` must be specified to make a layout');
14194 return;
14195 }
14196
14197 var name = options.name;
14198 var Layout = cy.extension('layout', name);
14199
14200 if (Layout == null) {
14201 error('No such layout `' + name + '` found. Did you forget to import it and `cytoscape.use()` it?');
14202 return;
14203 }
14204
14205 var eles;
14206
14207 if (string(options.eles)) {
14208 eles = cy.$(options.eles);
14209 } else {
14210 eles = options.eles != null ? options.eles : cy.$();
14211 }
14212
14213 var layout = new Layout(extend({}, options, {
14214 cy: cy,
14215 eles: eles
14216 }));
14217 return layout;
14218 }
14219};
14220corefn$3.createLayout = corefn$3.makeLayout = corefn$3.layout;
14221
14222var corefn$4 = {
14223 notify: function notify(eventName, eventEles) {
14224 var _p = this._private;
14225
14226 if (this.batching()) {
14227 _p.batchNotifications = _p.batchNotifications || {};
14228 var eles = _p.batchNotifications[eventName] = _p.batchNotifications[eventName] || this.collection();
14229
14230 if (eventEles != null) {
14231 eles.merge(eventEles);
14232 }
14233
14234 return; // notifications are disabled during batching
14235 }
14236
14237 if (!_p.notificationsEnabled) {
14238 return;
14239 } // exit on disabled
14240
14241
14242 var renderer = this.renderer(); // exit if destroy() called on core or renderer in between frames #1499 #1528
14243
14244 if (this.destroyed() || !renderer) {
14245 return;
14246 }
14247
14248 renderer.notify(eventName, eventEles);
14249 },
14250 notifications: function notifications(bool) {
14251 var p = this._private;
14252
14253 if (bool === undefined) {
14254 return p.notificationsEnabled;
14255 } else {
14256 p.notificationsEnabled = bool ? true : false;
14257 }
14258
14259 return this;
14260 },
14261 noNotifications: function noNotifications(callback) {
14262 this.notifications(false);
14263 callback();
14264 this.notifications(true);
14265 },
14266 batching: function batching() {
14267 return this._private.batchCount > 0;
14268 },
14269 startBatch: function startBatch() {
14270 var _p = this._private;
14271
14272 if (_p.batchCount == null) {
14273 _p.batchCount = 0;
14274 }
14275
14276 if (_p.batchCount === 0) {
14277 _p.batchStyleEles = this.collection();
14278 _p.batchNotifications = {};
14279 }
14280
14281 _p.batchCount++;
14282 return this;
14283 },
14284 endBatch: function endBatch() {
14285 var _p = this._private;
14286
14287 if (_p.batchCount === 0) {
14288 return this;
14289 }
14290
14291 _p.batchCount--;
14292
14293 if (_p.batchCount === 0) {
14294 // update style for dirty eles
14295 _p.batchStyleEles.updateStyle();
14296
14297 var renderer = this.renderer(); // notify the renderer of queued eles and event types
14298
14299 Object.keys(_p.batchNotifications).forEach(function (eventName) {
14300 var eles = _p.batchNotifications[eventName];
14301
14302 if (eles.empty()) {
14303 renderer.notify(eventName);
14304 } else {
14305 renderer.notify(eventName, eles);
14306 }
14307 });
14308 }
14309
14310 return this;
14311 },
14312 batch: function batch(callback) {
14313 this.startBatch();
14314 callback();
14315 this.endBatch();
14316 return this;
14317 },
14318 // for backwards compatibility
14319 batchData: function batchData(map) {
14320 var cy = this;
14321 return this.batch(function () {
14322 var ids = Object.keys(map);
14323
14324 for (var i = 0; i < ids.length; i++) {
14325 var id = ids[i];
14326 var data = map[id];
14327 var ele = cy.getElementById(id);
14328 ele.data(data);
14329 }
14330 });
14331 }
14332};
14333
14334var rendererDefaults = defaults({
14335 hideEdgesOnViewport: false,
14336 textureOnViewport: false,
14337 motionBlur: false,
14338 motionBlurOpacity: 0.05,
14339 pixelRatio: undefined,
14340 desktopTapThreshold: 4,
14341 touchTapThreshold: 8,
14342 wheelSensitivity: 1,
14343 debug: false,
14344 showFps: false
14345});
14346var corefn$5 = {
14347 renderTo: function renderTo(context, zoom, pan, pxRatio) {
14348 var r = this._private.renderer;
14349 r.renderTo(context, zoom, pan, pxRatio);
14350 return this;
14351 },
14352 renderer: function renderer() {
14353 return this._private.renderer;
14354 },
14355 forceRender: function forceRender() {
14356 this.notify('draw');
14357 return this;
14358 },
14359 resize: function resize() {
14360 this.invalidateSize();
14361 this.emitAndNotify('resize');
14362 return this;
14363 },
14364 initRenderer: function initRenderer(options) {
14365 var cy = this;
14366 var RendererProto = cy.extension('renderer', options.name);
14367
14368 if (RendererProto == null) {
14369 error("Can not initialise: No such renderer `".concat(options.name, "` found. Did you forget to import it and `cytoscape.use()` it?"));
14370 return;
14371 }
14372
14373 if (options.wheelSensitivity !== undefined) {
14374 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.");
14375 }
14376
14377 var rOpts = rendererDefaults(options);
14378 rOpts.cy = cy;
14379 cy._private.renderer = new RendererProto(rOpts);
14380 this.notify('init');
14381 },
14382 destroyRenderer: function destroyRenderer() {
14383 var cy = this;
14384 cy.notify('destroy'); // destroy the renderer
14385
14386 var domEle = cy.container();
14387
14388 if (domEle) {
14389 domEle._cyreg = null;
14390
14391 while (domEle.childNodes.length > 0) {
14392 domEle.removeChild(domEle.childNodes[0]);
14393 }
14394 }
14395
14396 cy._private.renderer = null; // to be extra safe, remove the ref
14397
14398 cy.mutableElements().forEach(function (ele) {
14399 var _p = ele._private;
14400 _p.rscratch = {};
14401 _p.rstyle = {};
14402 _p.animation.current = [];
14403 _p.animation.queue = [];
14404 });
14405 },
14406 onRender: function onRender(fn) {
14407 return this.on('render', fn);
14408 },
14409 offRender: function offRender(fn) {
14410 return this.off('render', fn);
14411 }
14412};
14413corefn$5.invalidateDimensions = corefn$5.resize;
14414
14415var corefn$6 = {
14416 // get a collection
14417 // - empty collection on no args
14418 // - collection of elements in the graph on selector arg
14419 // - guarantee a returned collection when elements or collection specified
14420 collection: function collection(eles, opts) {
14421 if (string(eles)) {
14422 return this.$(eles);
14423 } else if (elementOrCollection(eles)) {
14424 return eles.collection();
14425 } else if (array(eles)) {
14426 return new Collection(this, eles, opts);
14427 }
14428
14429 return new Collection(this);
14430 },
14431 nodes: function nodes(selector) {
14432 var nodes = this.$(function (ele) {
14433 return ele.isNode();
14434 });
14435
14436 if (selector) {
14437 return nodes.filter(selector);
14438 }
14439
14440 return nodes;
14441 },
14442 edges: function edges(selector) {
14443 var edges = this.$(function (ele) {
14444 return ele.isEdge();
14445 });
14446
14447 if (selector) {
14448 return edges.filter(selector);
14449 }
14450
14451 return edges;
14452 },
14453 // search the graph like jQuery
14454 $: function $(selector) {
14455 var eles = this._private.elements;
14456
14457 if (selector) {
14458 return eles.filter(selector);
14459 } else {
14460 return eles.spawnSelf();
14461 }
14462 },
14463 mutableElements: function mutableElements() {
14464 return this._private.elements;
14465 }
14466}; // aliases
14467
14468corefn$6.elements = corefn$6.filter = corefn$6.$;
14469
14470var styfn = {}; // keys for style blocks, e.g. ttfftt
14471
14472var TRUE = 't';
14473var FALSE = 'f'; // (potentially expensive calculation)
14474// apply the style to the element based on
14475// - its bypass
14476// - what selectors match it
14477
14478styfn.apply = function (eles) {
14479 var self = this;
14480 var _p = self._private;
14481 var cy = _p.cy;
14482 var updatedEles = cy.collection();
14483
14484 for (var ie = 0; ie < eles.length; ie++) {
14485 var ele = eles[ie];
14486 var cxtMeta = self.getContextMeta(ele);
14487
14488 if (cxtMeta.empty) {
14489 continue;
14490 }
14491
14492 var cxtStyle = self.getContextStyle(cxtMeta);
14493 var app = self.applyContextStyle(cxtMeta, cxtStyle, ele);
14494
14495 if (ele._private.appliedInitStyle) {
14496 self.updateTransitions(ele, app.diffProps);
14497 } else {
14498 ele._private.appliedInitStyle = true;
14499 }
14500
14501 var hintsDiff = self.updateStyleHints(ele);
14502
14503 if (hintsDiff) {
14504 updatedEles.push(ele);
14505 }
14506 } // for elements
14507
14508
14509 return updatedEles;
14510};
14511
14512styfn.getPropertiesDiff = function (oldCxtKey, newCxtKey) {
14513 var self = this;
14514 var cache = self._private.propDiffs = self._private.propDiffs || {};
14515 var dualCxtKey = oldCxtKey + '-' + newCxtKey;
14516 var cachedVal = cache[dualCxtKey];
14517
14518 if (cachedVal) {
14519 return cachedVal;
14520 }
14521
14522 var diffProps = [];
14523 var addedProp = {};
14524
14525 for (var i = 0; i < self.length; i++) {
14526 var cxt = self[i];
14527 var oldHasCxt = oldCxtKey[i] === TRUE;
14528 var newHasCxt = newCxtKey[i] === TRUE;
14529 var cxtHasDiffed = oldHasCxt !== newHasCxt;
14530 var cxtHasMappedProps = cxt.mappedProperties.length > 0;
14531
14532 if (cxtHasDiffed || newHasCxt && cxtHasMappedProps) {
14533 var props = void 0;
14534
14535 if (cxtHasDiffed && cxtHasMappedProps) {
14536 props = cxt.properties; // suffices b/c mappedProperties is a subset of properties
14537 } else if (cxtHasDiffed) {
14538 props = cxt.properties; // need to check them all
14539 } else if (cxtHasMappedProps) {
14540 props = cxt.mappedProperties; // only need to check mapped
14541 }
14542
14543 for (var j = 0; j < props.length; j++) {
14544 var prop = props[j];
14545 var name = prop.name; // if a later context overrides this property, then the fact that this context has switched/diffed doesn't matter
14546 // (semi expensive check since it makes this function O(n^2) on context length, but worth it since overall result
14547 // is cached)
14548
14549 var laterCxtOverrides = false;
14550
14551 for (var k = i + 1; k < self.length; k++) {
14552 var laterCxt = self[k];
14553 var hasLaterCxt = newCxtKey[k] === TRUE;
14554
14555 if (!hasLaterCxt) {
14556 continue;
14557 } // can't override unless the context is active
14558
14559
14560 laterCxtOverrides = laterCxt.properties[prop.name] != null;
14561
14562 if (laterCxtOverrides) {
14563 break;
14564 } // exit early as long as one later context overrides
14565
14566 }
14567
14568 if (!addedProp[name] && !laterCxtOverrides) {
14569 addedProp[name] = true;
14570 diffProps.push(name);
14571 }
14572 } // for props
14573
14574 } // if
14575
14576 } // for contexts
14577
14578
14579 cache[dualCxtKey] = diffProps;
14580 return diffProps;
14581};
14582
14583styfn.getContextMeta = function (ele) {
14584 var self = this;
14585 var cxtKey = '';
14586 var diffProps;
14587 var prevKey = ele._private.styleCxtKey || ''; // get the cxt key
14588
14589 for (var i = 0; i < self.length; i++) {
14590 var context = self[i];
14591 var contextSelectorMatches = context.selector && context.selector.matches(ele); // NB: context.selector may be null for 'core'
14592
14593 if (contextSelectorMatches) {
14594 cxtKey += TRUE;
14595 } else {
14596 cxtKey += FALSE;
14597 }
14598 } // for context
14599
14600
14601 diffProps = self.getPropertiesDiff(prevKey, cxtKey);
14602 ele._private.styleCxtKey = cxtKey;
14603 return {
14604 key: cxtKey,
14605 diffPropNames: diffProps,
14606 empty: diffProps.length === 0
14607 };
14608}; // gets a computed ele style object based on matched contexts
14609
14610
14611styfn.getContextStyle = function (cxtMeta) {
14612 var cxtKey = cxtMeta.key;
14613 var self = this;
14614 var cxtStyles = this._private.contextStyles = this._private.contextStyles || {}; // if already computed style, returned cached copy
14615
14616 if (cxtStyles[cxtKey]) {
14617 return cxtStyles[cxtKey];
14618 }
14619
14620 var style = {
14621 _private: {
14622 key: cxtKey
14623 }
14624 };
14625
14626 for (var i = 0; i < self.length; i++) {
14627 var cxt = self[i];
14628 var hasCxt = cxtKey[i] === TRUE;
14629
14630 if (!hasCxt) {
14631 continue;
14632 }
14633
14634 for (var j = 0; j < cxt.properties.length; j++) {
14635 var prop = cxt.properties[j];
14636 style[prop.name] = prop;
14637 }
14638 }
14639
14640 cxtStyles[cxtKey] = style;
14641 return style;
14642};
14643
14644styfn.applyContextStyle = function (cxtMeta, cxtStyle, ele) {
14645 var self = this;
14646 var diffProps = cxtMeta.diffPropNames;
14647 var retDiffProps = {};
14648 var types = self.types;
14649
14650 for (var i = 0; i < diffProps.length; i++) {
14651 var diffPropName = diffProps[i];
14652 var cxtProp = cxtStyle[diffPropName];
14653 var eleProp = ele.pstyle(diffPropName);
14654
14655 if (!cxtProp) {
14656 // no context prop means delete
14657 if (!eleProp) {
14658 continue; // no existing prop means nothing needs to be removed
14659 // nb affects initial application on mapped values like control-point-distances
14660 } else if (eleProp.bypass) {
14661 cxtProp = {
14662 name: diffPropName,
14663 deleteBypassed: true
14664 };
14665 } else {
14666 cxtProp = {
14667 name: diffPropName,
14668 "delete": true
14669 };
14670 }
14671 } // save cycles when the context prop doesn't need to be applied
14672
14673
14674 if (eleProp === cxtProp) {
14675 continue;
14676 } // save cycles when a mapped context prop doesn't need to be applied
14677
14678
14679 if (cxtProp.mapped === types.fn // context prop is function mapper
14680 && eleProp != null // some props can be null even by default (e.g. a prop that overrides another one)
14681 && eleProp.mapping != null // ele prop is a concrete value from from a mapper
14682 && eleProp.mapping.value === cxtProp.value // the current prop on the ele is a flat prop value for the function mapper
14683 ) {
14684 // NB don't write to cxtProp, as it's shared among eles (stored in stylesheet)
14685 var mapping = eleProp.mapping; // can write to mapping, as it's a per-ele copy
14686
14687 var fnValue = mapping.fnValue = cxtProp.value(ele); // temporarily cache the value in case of a miss
14688
14689 if (fnValue === mapping.prevFnValue) {
14690 continue;
14691 }
14692 }
14693
14694 var retDiffProp = retDiffProps[diffPropName] = {
14695 prev: eleProp
14696 };
14697 self.applyParsedProperty(ele, cxtProp);
14698 retDiffProp.next = ele.pstyle(diffPropName);
14699
14700 if (retDiffProp.next && retDiffProp.next.bypass) {
14701 retDiffProp.next = retDiffProp.next.bypassed;
14702 }
14703 }
14704
14705 return {
14706 diffProps: retDiffProps
14707 };
14708};
14709
14710styfn.updateStyleHints = function (ele) {
14711 var _p = ele._private;
14712 var self = this;
14713 var propNames = self.propertyGroupNames;
14714 var propGrKeys = self.propertyGroupKeys;
14715
14716 var propHash = function propHash(ele, propNames, seedKey) {
14717 return self.getPropertiesHash(ele, propNames, seedKey);
14718 };
14719
14720 var oldStyleKey = _p.styleKey;
14721
14722 if (ele.removed()) {
14723 return false;
14724 }
14725
14726 var isNode = _p.group === 'nodes'; // get the style key hashes per prop group
14727 // but lazily -- only use non-default prop values to reduce the number of hashes
14728 //
14729
14730 var overriddenStyles = ele._private.style;
14731 propNames = Object.keys(overriddenStyles);
14732
14733 for (var i = 0; i < propGrKeys.length; i++) {
14734 var grKey = propGrKeys[i];
14735 _p.styleKeys[grKey] = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14736 }
14737
14738 var updateGrKey1 = function updateGrKey1(val, grKey) {
14739 return _p.styleKeys[grKey][0] = hashInt(val, _p.styleKeys[grKey][0]);
14740 };
14741
14742 var updateGrKey2 = function updateGrKey2(val, grKey) {
14743 return _p.styleKeys[grKey][1] = hashIntAlt(val, _p.styleKeys[grKey][1]);
14744 };
14745
14746 var updateGrKey = function updateGrKey(val, grKey) {
14747 updateGrKey1(val, grKey);
14748 updateGrKey2(val, grKey);
14749 };
14750
14751 var updateGrKeyWStr = function updateGrKeyWStr(strVal, grKey) {
14752 for (var j = 0; j < strVal.length; j++) {
14753 var ch = strVal.charCodeAt(j);
14754 updateGrKey1(ch, grKey);
14755 updateGrKey2(ch, grKey);
14756 }
14757 }; // - hashing works on 32 bit ints b/c we use bitwise ops
14758 // - small numbers get cut off (e.g. 0.123 is seen as 0 by the hashing function)
14759 // - raise up small numbers so more significant digits are seen by hashing
14760 // - make small numbers larger than a normal value to avoid collisions
14761 // - works in practice and it's relatively cheap
14762
14763
14764 var N = 2000000000;
14765
14766 var cleanNum = function cleanNum(val) {
14767 return -128 < val && val < 128 && Math.floor(val) !== val ? N - (val * 1024 | 0) : val;
14768 };
14769
14770 for (var _i = 0; _i < propNames.length; _i++) {
14771 var name = propNames[_i];
14772 var parsedProp = overriddenStyles[name];
14773
14774 if (parsedProp == null) {
14775 continue;
14776 }
14777
14778 var propInfo = this.properties[name];
14779 var type = propInfo.type;
14780 var _grKey = propInfo.groupKey;
14781 var normalizedNumberVal = void 0;
14782
14783 if (propInfo.hashOverride != null) {
14784 normalizedNumberVal = propInfo.hashOverride(ele, parsedProp);
14785 } else if (parsedProp.pfValue != null) {
14786 normalizedNumberVal = parsedProp.pfValue;
14787 } // might not be a number if it allows enums
14788
14789
14790 var numberVal = propInfo.enums == null ? parsedProp.value : null;
14791 var haveNormNum = normalizedNumberVal != null;
14792 var haveUnitedNum = numberVal != null;
14793 var haveNum = haveNormNum || haveUnitedNum;
14794 var units = parsedProp.units; // numbers are cheaper to hash than strings
14795 // 1 hash op vs n hash ops (for length n string)
14796
14797 if (type.number && haveNum && !type.multiple) {
14798 var v = haveNormNum ? normalizedNumberVal : numberVal;
14799 updateGrKey(cleanNum(v), _grKey);
14800
14801 if (!haveNormNum && units != null) {
14802 updateGrKeyWStr(units, _grKey);
14803 }
14804 } else {
14805 updateGrKeyWStr(parsedProp.strValue, _grKey);
14806 }
14807 } // overall style key
14808 //
14809
14810
14811 var hash = [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT];
14812
14813 for (var _i2 = 0; _i2 < propGrKeys.length; _i2++) {
14814 var _grKey2 = propGrKeys[_i2];
14815 var grHash = _p.styleKeys[_grKey2];
14816 hash[0] = hashInt(grHash[0], hash[0]);
14817 hash[1] = hashIntAlt(grHash[1], hash[1]);
14818 }
14819
14820 _p.styleKey = combineHashes(hash[0], hash[1]); // label dims
14821 //
14822
14823 var sk = _p.styleKeys;
14824 _p.labelDimsKey = combineHashesArray(sk.labelDimensions);
14825 var labelKeys = propHash(ele, ['label'], sk.labelDimensions);
14826 _p.labelKey = combineHashesArray(labelKeys);
14827 _p.labelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, labelKeys));
14828
14829 if (!isNode) {
14830 var sourceLabelKeys = propHash(ele, ['source-label'], sk.labelDimensions);
14831 _p.sourceLabelKey = combineHashesArray(sourceLabelKeys);
14832 _p.sourceLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, sourceLabelKeys));
14833 var targetLabelKeys = propHash(ele, ['target-label'], sk.labelDimensions);
14834 _p.targetLabelKey = combineHashesArray(targetLabelKeys);
14835 _p.targetLabelStyleKey = combineHashesArray(hashArrays(sk.commonLabel, targetLabelKeys));
14836 } // node
14837 //
14838
14839
14840 if (isNode) {
14841 var _p$styleKeys = _p.styleKeys,
14842 nodeBody = _p$styleKeys.nodeBody,
14843 nodeBorder = _p$styleKeys.nodeBorder,
14844 backgroundImage = _p$styleKeys.backgroundImage,
14845 compound = _p$styleKeys.compound,
14846 pie = _p$styleKeys.pie;
14847 var nodeKeys = [nodeBody, nodeBorder, backgroundImage, compound, pie].filter(function (k) {
14848 return k != null;
14849 }).reduce(hashArrays, [DEFAULT_HASH_SEED, DEFAULT_HASH_SEED_ALT]);
14850 _p.nodeKey = combineHashesArray(nodeKeys);
14851 _p.hasPie = pie != null && pie[0] !== DEFAULT_HASH_SEED && pie[1] !== DEFAULT_HASH_SEED_ALT;
14852 }
14853
14854 return oldStyleKey !== _p.styleKey;
14855};
14856
14857styfn.clearStyleHints = function (ele) {
14858 var _p = ele._private;
14859 _p.styleCxtKey = '';
14860 _p.styleKeys = {};
14861 _p.styleKey = null;
14862 _p.labelKey = null;
14863 _p.labelStyleKey = null;
14864 _p.sourceLabelKey = null;
14865 _p.sourceLabelStyleKey = null;
14866 _p.targetLabelKey = null;
14867 _p.targetLabelStyleKey = null;
14868 _p.nodeKey = null;
14869 _p.hasPie = null;
14870}; // apply a property to the style (for internal use)
14871// returns whether application was successful
14872//
14873// now, this function flattens the property, and here's how:
14874//
14875// for parsedProp:{ bypass: true, deleteBypass: true }
14876// no property is generated, instead the bypass property in the
14877// element's style is replaced by what's pointed to by the `bypassed`
14878// field in the bypass property (i.e. restoring the property the
14879// bypass was overriding)
14880//
14881// for parsedProp:{ mapped: truthy }
14882// the generated flattenedProp:{ mapping: prop }
14883//
14884// for parsedProp:{ bypass: true }
14885// the generated flattenedProp:{ bypassed: parsedProp }
14886
14887
14888styfn.applyParsedProperty = function (ele, parsedProp) {
14889 var self = this;
14890 var prop = parsedProp;
14891 var style = ele._private.style;
14892 var flatProp;
14893 var types = self.types;
14894 var type = self.properties[prop.name].type;
14895 var propIsBypass = prop.bypass;
14896 var origProp = style[prop.name];
14897 var origPropIsBypass = origProp && origProp.bypass;
14898 var _p = ele._private;
14899 var flatPropMapping = 'mapping';
14900
14901 var getVal = function getVal(p) {
14902 if (p == null) {
14903 return null;
14904 } else if (p.pfValue != null) {
14905 return p.pfValue;
14906 } else {
14907 return p.value;
14908 }
14909 };
14910
14911 var checkTriggers = function checkTriggers() {
14912 var fromVal = getVal(origProp);
14913 var toVal = getVal(prop);
14914 self.checkTriggers(ele, prop.name, fromVal, toVal);
14915 };
14916
14917 if (prop && prop.name.substr(0, 3) === 'pie') {
14918 warn('The pie style properties are deprecated. Create charts using background images instead.');
14919 } // edge sanity checks to prevent the client from making serious mistakes
14920
14921
14922 if (parsedProp.name === 'curve-style' && ele.isEdge() && ( // loops must be bundled beziers
14923 parsedProp.value !== 'bezier' && ele.isLoop() || // edges connected to compound nodes can not be haystacks
14924 parsedProp.value === 'haystack' && (ele.source().isParent() || ele.target().isParent()))) {
14925 prop = parsedProp = this.parse(parsedProp.name, 'bezier', propIsBypass);
14926 }
14927
14928 if (prop["delete"]) {
14929 // delete the property and use the default value on falsey value
14930 style[prop.name] = undefined;
14931 checkTriggers();
14932 return true;
14933 }
14934
14935 if (prop.deleteBypassed) {
14936 // delete the property that the
14937 if (!origProp) {
14938 checkTriggers();
14939 return true; // can't delete if no prop
14940 } else if (origProp.bypass) {
14941 // delete bypassed
14942 origProp.bypassed = undefined;
14943 checkTriggers();
14944 return true;
14945 } else {
14946 return false; // we're unsuccessful deleting the bypassed
14947 }
14948 } // check if we need to delete the current bypass
14949
14950
14951 if (prop.deleteBypass) {
14952 // then this property is just here to indicate we need to delete
14953 if (!origProp) {
14954 checkTriggers();
14955 return true; // property is already not defined
14956 } else if (origProp.bypass) {
14957 // then replace the bypass property with the original
14958 // because the bypassed property was already applied (and therefore parsed), we can just replace it (no reapplying necessary)
14959 style[prop.name] = origProp.bypassed;
14960 checkTriggers();
14961 return true;
14962 } else {
14963 return false; // we're unsuccessful deleting the bypass
14964 }
14965 }
14966
14967 var printMappingErr = function printMappingErr() {
14968 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');
14969 }; // put the property in the style objects
14970
14971
14972 switch (prop.mapped) {
14973 // flatten the property if mapped
14974 case types.mapData:
14975 {
14976 // flatten the field (e.g. data.foo.bar)
14977 var fields = prop.field.split('.');
14978 var fieldVal = _p.data;
14979
14980 for (var i = 0; i < fields.length && fieldVal; i++) {
14981 var field = fields[i];
14982 fieldVal = fieldVal[field];
14983 }
14984
14985 if (fieldVal == null) {
14986 printMappingErr();
14987 return false;
14988 }
14989
14990 var percent;
14991
14992 if (!number(fieldVal)) {
14993 // then don't apply and fall back on the existing style
14994 warn('Do not use continuous mappers without specifying numeric data (i.e. `' + prop.field + ': ' + fieldVal + '` for `' + ele.id() + '` is non-numeric)');
14995 return false;
14996 } else {
14997 var fieldWidth = prop.fieldMax - prop.fieldMin;
14998
14999 if (fieldWidth === 0) {
15000 // safety check -- not strictly necessary as no props of zero range should be passed here
15001 percent = 0;
15002 } else {
15003 percent = (fieldVal - prop.fieldMin) / fieldWidth;
15004 }
15005 } // make sure to bound percent value
15006
15007
15008 if (percent < 0) {
15009 percent = 0;
15010 } else if (percent > 1) {
15011 percent = 1;
15012 }
15013
15014 if (type.color) {
15015 var r1 = prop.valueMin[0];
15016 var r2 = prop.valueMax[0];
15017 var g1 = prop.valueMin[1];
15018 var g2 = prop.valueMax[1];
15019 var b1 = prop.valueMin[2];
15020 var b2 = prop.valueMax[2];
15021 var a1 = prop.valueMin[3] == null ? 1 : prop.valueMin[3];
15022 var a2 = prop.valueMax[3] == null ? 1 : prop.valueMax[3];
15023 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)];
15024 flatProp = {
15025 // colours are simple, so just create the flat property instead of expensive string parsing
15026 bypass: prop.bypass,
15027 // we're a bypass if the mapping property is a bypass
15028 name: prop.name,
15029 value: clr,
15030 strValue: 'rgb(' + clr[0] + ', ' + clr[1] + ', ' + clr[2] + ')'
15031 };
15032 } else if (type.number) {
15033 var calcValue = prop.valueMin + (prop.valueMax - prop.valueMin) * percent;
15034 flatProp = this.parse(prop.name, calcValue, prop.bypass, flatPropMapping);
15035 } else {
15036 return false; // can only map to colours and numbers
15037 }
15038
15039 if (!flatProp) {
15040 // if we can't flatten the property, then don't apply the property and fall back on the existing style
15041 printMappingErr();
15042 return false;
15043 }
15044
15045 flatProp.mapping = prop; // keep a reference to the mapping
15046
15047 prop = flatProp; // the flattened (mapped) property is the one we want
15048
15049 break;
15050 }
15051 // direct mapping
15052
15053 case types.data:
15054 {
15055 // flatten the field (e.g. data.foo.bar)
15056 var _fields = prop.field.split('.');
15057
15058 var _fieldVal = _p.data;
15059
15060 for (var _i3 = 0; _i3 < _fields.length && _fieldVal; _i3++) {
15061 var _field = _fields[_i3];
15062 _fieldVal = _fieldVal[_field];
15063 }
15064
15065 if (_fieldVal != null) {
15066 flatProp = this.parse(prop.name, _fieldVal, prop.bypass, flatPropMapping);
15067 }
15068
15069 if (!flatProp) {
15070 // if we can't flatten the property, then don't apply and fall back on the existing style
15071 printMappingErr();
15072 return false;
15073 }
15074
15075 flatProp.mapping = prop; // keep a reference to the mapping
15076
15077 prop = flatProp; // the flattened (mapped) property is the one we want
15078
15079 break;
15080 }
15081
15082 case types.fn:
15083 {
15084 var fn = prop.value;
15085 var fnRetVal = prop.fnValue != null ? prop.fnValue : fn(ele); // check for cached value before calling function
15086
15087 prop.prevFnValue = fnRetVal;
15088
15089 if (fnRetVal == null) {
15090 warn('Custom function mappers may not return null (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is null)');
15091 return false;
15092 }
15093
15094 flatProp = this.parse(prop.name, fnRetVal, prop.bypass, flatPropMapping);
15095
15096 if (!flatProp) {
15097 warn('Custom function mappers may not return invalid values for the property type (i.e. `' + prop.name + '` for ele `' + ele.id() + '` is invalid)');
15098 return false;
15099 }
15100
15101 flatProp.mapping = copy(prop); // keep a reference to the mapping
15102
15103 prop = flatProp; // the flattened (mapped) property is the one we want
15104
15105 break;
15106 }
15107
15108 case undefined:
15109 break;
15110 // just set the property
15111
15112 default:
15113 return false;
15114 // not a valid mapping
15115 } // if the property is a bypass property, then link the resultant property to the original one
15116
15117
15118 if (propIsBypass) {
15119 if (origPropIsBypass) {
15120 // then this bypass overrides the existing one
15121 prop.bypassed = origProp.bypassed; // steal bypassed prop from old bypass
15122 } else {
15123 // then link the orig prop to the new bypass
15124 prop.bypassed = origProp;
15125 }
15126
15127 style[prop.name] = prop; // and set
15128 } else {
15129 // prop is not bypass
15130 if (origPropIsBypass) {
15131 // then keep the orig prop (since it's a bypass) and link to the new prop
15132 origProp.bypassed = prop;
15133 } else {
15134 // then just replace the old prop with the new one
15135 style[prop.name] = prop;
15136 }
15137 }
15138
15139 checkTriggers();
15140 return true;
15141};
15142
15143styfn.cleanElements = function (eles, keepBypasses) {
15144 for (var i = 0; i < eles.length; i++) {
15145 var ele = eles[i];
15146 this.clearStyleHints(ele);
15147 ele.dirtyCompoundBoundsCache();
15148 ele.dirtyBoundingBoxCache();
15149
15150 if (!keepBypasses) {
15151 ele._private.style = {};
15152 } else {
15153 var style = ele._private.style;
15154 var propNames = Object.keys(style);
15155
15156 for (var j = 0; j < propNames.length; j++) {
15157 var propName = propNames[j];
15158 var eleProp = style[propName];
15159
15160 if (eleProp != null) {
15161 if (eleProp.bypass) {
15162 eleProp.bypassed = null;
15163 } else {
15164 style[propName] = null;
15165 }
15166 }
15167 }
15168 }
15169 }
15170}; // updates the visual style for all elements (useful for manual style modification after init)
15171
15172
15173styfn.update = function () {
15174 var cy = this._private.cy;
15175 var eles = cy.mutableElements();
15176 eles.updateStyle();
15177}; // diffProps : { name => { prev, next } }
15178
15179
15180styfn.updateTransitions = function (ele, diffProps) {
15181 var self = this;
15182 var _p = ele._private;
15183 var props = ele.pstyle('transition-property').value;
15184 var duration = ele.pstyle('transition-duration').pfValue;
15185 var delay = ele.pstyle('transition-delay').pfValue;
15186
15187 if (props.length > 0 && duration > 0) {
15188 var style = {}; // build up the style to animate towards
15189
15190 var anyPrev = false;
15191
15192 for (var i = 0; i < props.length; i++) {
15193 var prop = props[i];
15194 var styProp = ele.pstyle(prop);
15195 var diffProp = diffProps[prop];
15196
15197 if (!diffProp) {
15198 continue;
15199 }
15200
15201 var prevProp = diffProp.prev;
15202 var fromProp = prevProp;
15203 var toProp = diffProp.next != null ? diffProp.next : styProp;
15204 var diff = false;
15205 var initVal = void 0;
15206 var initDt = 0.000001; // delta time % value for initVal (allows animating out of init zero opacity)
15207
15208 if (!fromProp) {
15209 continue;
15210 } // consider px values
15211
15212
15213 if (number(fromProp.pfValue) && number(toProp.pfValue)) {
15214 diff = toProp.pfValue - fromProp.pfValue; // nonzero is truthy
15215
15216 initVal = fromProp.pfValue + initDt * diff; // consider numerical values
15217 } else if (number(fromProp.value) && number(toProp.value)) {
15218 diff = toProp.value - fromProp.value; // nonzero is truthy
15219
15220 initVal = fromProp.value + initDt * diff; // consider colour values
15221 } else if (array(fromProp.value) && array(toProp.value)) {
15222 diff = fromProp.value[0] !== toProp.value[0] || fromProp.value[1] !== toProp.value[1] || fromProp.value[2] !== toProp.value[2];
15223 initVal = fromProp.strValue;
15224 } // the previous value is good for an animation only if it's different
15225
15226
15227 if (diff) {
15228 style[prop] = toProp.strValue; // to val
15229
15230 this.applyBypass(ele, prop, initVal); // from val
15231
15232 anyPrev = true;
15233 }
15234 } // end if props allow ani
15235 // can't transition if there's nothing previous to transition from
15236
15237
15238 if (!anyPrev) {
15239 return;
15240 }
15241
15242 _p.transitioning = true;
15243 new Promise$1(function (resolve) {
15244 if (delay > 0) {
15245 ele.delayAnimation(delay).play().promise().then(resolve);
15246 } else {
15247 resolve();
15248 }
15249 }).then(function () {
15250 return ele.animation({
15251 style: style,
15252 duration: duration,
15253 easing: ele.pstyle('transition-timing-function').value,
15254 queue: false
15255 }).play().promise();
15256 }).then(function () {
15257 // if( !isBypass ){
15258 self.removeBypasses(ele, props);
15259 ele.emitAndNotify('style'); // }
15260
15261 _p.transitioning = false;
15262 });
15263 } else if (_p.transitioning) {
15264 this.removeBypasses(ele, props);
15265 ele.emitAndNotify('style');
15266 _p.transitioning = false;
15267 }
15268};
15269
15270styfn.checkTrigger = function (ele, name, fromValue, toValue, getTrigger, onTrigger) {
15271 var prop = this.properties[name];
15272 var triggerCheck = getTrigger(prop);
15273
15274 if (triggerCheck != null && triggerCheck(fromValue, toValue)) {
15275 onTrigger(prop);
15276 }
15277};
15278
15279styfn.checkZOrderTrigger = function (ele, name, fromValue, toValue) {
15280 var _this = this;
15281
15282 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15283 return prop.triggersZOrder;
15284 }, function () {
15285 _this._private.cy.notify('zorder', ele);
15286 });
15287};
15288
15289styfn.checkBoundsTrigger = function (ele, name, fromValue, toValue) {
15290 this.checkTrigger(ele, name, fromValue, toValue, function (prop) {
15291 return prop.triggersBounds;
15292 }, function (prop) {
15293 ele.dirtyCompoundBoundsCache();
15294 ele.dirtyBoundingBoxCache(); // if the prop change makes the bb of pll bezier edges invalid,
15295 // then dirty the pll edge bb cache as well
15296
15297 if ( // only for beziers -- so performance of other edges isn't affected
15298 name === 'curve-style' && (fromValue === 'bezier' || toValue === 'bezier') && prop.triggersBoundsOfParallelBeziers) {
15299 ele.parallelEdges().forEach(function (pllEdge) {
15300 if (pllEdge.isBundledBezier()) {
15301 pllEdge.dirtyBoundingBoxCache();
15302 }
15303 });
15304 }
15305 });
15306};
15307
15308styfn.checkTriggers = function (ele, name, fromValue, toValue) {
15309 ele.dirtyStyleCache();
15310 this.checkZOrderTrigger(ele, name, fromValue, toValue);
15311 this.checkBoundsTrigger(ele, name, fromValue, toValue);
15312};
15313
15314var styfn$1 = {}; // bypasses are applied to an existing style on an element, and just tacked on temporarily
15315// returns true iff application was successful for at least 1 specified property
15316
15317styfn$1.applyBypass = function (eles, name, value, updateTransitions) {
15318 var self = this;
15319 var props = [];
15320 var isBypass = true; // put all the properties (can specify one or many) in an array after parsing them
15321
15322 if (name === '*' || name === '**') {
15323 // apply to all property names
15324 if (value !== undefined) {
15325 for (var i = 0; i < self.properties.length; i++) {
15326 var prop = self.properties[i];
15327 var _name = prop.name;
15328 var parsedProp = this.parse(_name, value, true);
15329
15330 if (parsedProp) {
15331 props.push(parsedProp);
15332 }
15333 }
15334 }
15335 } else if (string(name)) {
15336 // then parse the single property
15337 var _parsedProp = this.parse(name, value, true);
15338
15339 if (_parsedProp) {
15340 props.push(_parsedProp);
15341 }
15342 } else if (plainObject(name)) {
15343 // then parse each property
15344 var specifiedProps = name;
15345 updateTransitions = value;
15346 var names = Object.keys(specifiedProps);
15347
15348 for (var _i = 0; _i < names.length; _i++) {
15349 var _name2 = names[_i];
15350 var _value = specifiedProps[_name2];
15351
15352 if (_value === undefined) {
15353 // try camel case name too
15354 _value = specifiedProps[dash2camel(_name2)];
15355 }
15356
15357 if (_value !== undefined) {
15358 var _parsedProp2 = this.parse(_name2, _value, true);
15359
15360 if (_parsedProp2) {
15361 props.push(_parsedProp2);
15362 }
15363 }
15364 }
15365 } else {
15366 // can't do anything without well defined properties
15367 return false;
15368 } // we've failed if there are no valid properties
15369
15370
15371 if (props.length === 0) {
15372 return false;
15373 } // now, apply the bypass properties on the elements
15374
15375
15376 var ret = false; // return true if at least one succesful bypass applied
15377
15378 for (var _i2 = 0; _i2 < eles.length; _i2++) {
15379 // for each ele
15380 var ele = eles[_i2];
15381 var diffProps = {};
15382 var diffProp = void 0;
15383
15384 for (var j = 0; j < props.length; j++) {
15385 // for each prop
15386 var _prop = props[j];
15387
15388 if (updateTransitions) {
15389 var prevProp = ele.pstyle(_prop.name);
15390 diffProp = diffProps[_prop.name] = {
15391 prev: prevProp
15392 };
15393 }
15394
15395 ret = this.applyParsedProperty(ele, copy(_prop)) || ret;
15396
15397 if (updateTransitions) {
15398 diffProp.next = ele.pstyle(_prop.name);
15399 }
15400 } // for props
15401
15402
15403 if (ret) {
15404 this.updateStyleHints(ele);
15405 }
15406
15407 if (updateTransitions) {
15408 this.updateTransitions(ele, diffProps, isBypass);
15409 }
15410 } // for eles
15411
15412
15413 return ret;
15414}; // only useful in specific cases like animation
15415
15416
15417styfn$1.overrideBypass = function (eles, name, value) {
15418 name = camel2dash(name);
15419
15420 for (var i = 0; i < eles.length; i++) {
15421 var ele = eles[i];
15422 var prop = ele._private.style[name];
15423 var type = this.properties[name].type;
15424 var isColor = type.color;
15425 var isMulti = type.mutiple;
15426 var oldValue = !prop ? null : prop.pfValue != null ? prop.pfValue : prop.value;
15427
15428 if (!prop || !prop.bypass) {
15429 // need a bypass if one doesn't exist
15430 this.applyBypass(ele, name, value);
15431 } else {
15432 prop.value = value;
15433
15434 if (prop.pfValue != null) {
15435 prop.pfValue = value;
15436 }
15437
15438 if (isColor) {
15439 prop.strValue = 'rgb(' + value.join(',') + ')';
15440 } else if (isMulti) {
15441 prop.strValue = value.join(' ');
15442 } else {
15443 prop.strValue = '' + value;
15444 }
15445
15446 this.updateStyleHints(ele);
15447 }
15448
15449 this.checkTriggers(ele, name, oldValue, value);
15450 }
15451};
15452
15453styfn$1.removeAllBypasses = function (eles, updateTransitions) {
15454 return this.removeBypasses(eles, this.propertyNames, updateTransitions);
15455};
15456
15457styfn$1.removeBypasses = function (eles, props, updateTransitions) {
15458 var isBypass = true;
15459
15460 for (var j = 0; j < eles.length; j++) {
15461 var ele = eles[j];
15462 var diffProps = {};
15463
15464 for (var i = 0; i < props.length; i++) {
15465 var name = props[i];
15466 var prop = this.properties[name];
15467 var prevProp = ele.pstyle(prop.name);
15468
15469 if (!prevProp || !prevProp.bypass) {
15470 // if a bypass doesn't exist for the prop, nothing needs to be removed
15471 continue;
15472 }
15473
15474 var value = ''; // empty => remove bypass
15475
15476 var parsedProp = this.parse(name, value, true);
15477 var diffProp = diffProps[prop.name] = {
15478 prev: prevProp
15479 };
15480 this.applyParsedProperty(ele, parsedProp);
15481 diffProp.next = ele.pstyle(prop.name);
15482 } // for props
15483
15484
15485 this.updateStyleHints(ele);
15486
15487 if (updateTransitions) {
15488 this.updateTransitions(ele, diffProps, isBypass);
15489 }
15490 } // for eles
15491
15492};
15493
15494var styfn$2 = {}; // gets what an em size corresponds to in pixels relative to a dom element
15495
15496styfn$2.getEmSizeInPixels = function () {
15497 var px = this.containerCss('font-size');
15498
15499 if (px != null) {
15500 return parseFloat(px);
15501 } else {
15502 return 1; // for headless
15503 }
15504}; // gets css property from the core container
15505
15506
15507styfn$2.containerCss = function (propName) {
15508 var cy = this._private.cy;
15509 var domElement = cy.container();
15510
15511 if (window$1 && domElement && window$1.getComputedStyle) {
15512 return window$1.getComputedStyle(domElement).getPropertyValue(propName);
15513 }
15514};
15515
15516var styfn$3 = {}; // gets the rendered style for an element
15517
15518styfn$3.getRenderedStyle = function (ele, prop) {
15519 if (prop) {
15520 return this.getStylePropertyValue(ele, prop, true);
15521 } else {
15522 return this.getRawStyle(ele, true);
15523 }
15524}; // gets the raw style for an element
15525
15526
15527styfn$3.getRawStyle = function (ele, isRenderedVal) {
15528 var self = this;
15529 ele = ele[0]; // insure it's an element
15530
15531 if (ele) {
15532 var rstyle = {};
15533
15534 for (var i = 0; i < self.properties.length; i++) {
15535 var prop = self.properties[i];
15536 var val = self.getStylePropertyValue(ele, prop.name, isRenderedVal);
15537
15538 if (val != null) {
15539 rstyle[prop.name] = val;
15540 rstyle[dash2camel(prop.name)] = val;
15541 }
15542 }
15543
15544 return rstyle;
15545 }
15546};
15547
15548styfn$3.getIndexedStyle = function (ele, property, subproperty, index) {
15549 var pstyle = ele.pstyle(property)[subproperty][index];
15550 return pstyle != null ? pstyle : ele.cy().style().getDefaultProperty(property)[subproperty][0];
15551};
15552
15553styfn$3.getStylePropertyValue = function (ele, propName, isRenderedVal) {
15554 var self = this;
15555 ele = ele[0]; // insure it's an element
15556
15557 if (ele) {
15558 var prop = self.properties[propName];
15559
15560 if (prop.alias) {
15561 prop = prop.pointsTo;
15562 }
15563
15564 var type = prop.type;
15565 var styleProp = ele.pstyle(prop.name);
15566
15567 if (styleProp) {
15568 var value = styleProp.value,
15569 units = styleProp.units,
15570 strValue = styleProp.strValue;
15571
15572 if (isRenderedVal && type.number && value != null && number(value)) {
15573 var zoom = ele.cy().zoom();
15574
15575 var getRenderedValue = function getRenderedValue(val) {
15576 return val * zoom;
15577 };
15578
15579 var getValueStringWithUnits = function getValueStringWithUnits(val, units) {
15580 return getRenderedValue(val) + units;
15581 };
15582
15583 var isArrayValue = array(value);
15584 var haveUnits = isArrayValue ? units.every(function (u) {
15585 return u != null;
15586 }) : units != null;
15587
15588 if (haveUnits) {
15589 if (isArrayValue) {
15590 return value.map(function (v, i) {
15591 return getValueStringWithUnits(v, units[i]);
15592 }).join(' ');
15593 } else {
15594 return getValueStringWithUnits(value, units);
15595 }
15596 } else {
15597 if (isArrayValue) {
15598 return value.map(function (v) {
15599 return string(v) ? v : '' + getRenderedValue(v);
15600 }).join(' ');
15601 } else {
15602 return '' + getRenderedValue(value);
15603 }
15604 }
15605 } else if (strValue != null) {
15606 return strValue;
15607 }
15608 }
15609
15610 return null;
15611 }
15612};
15613
15614styfn$3.getAnimationStartStyle = function (ele, aniProps) {
15615 var rstyle = {};
15616
15617 for (var i = 0; i < aniProps.length; i++) {
15618 var aniProp = aniProps[i];
15619 var name = aniProp.name;
15620 var styleProp = ele.pstyle(name);
15621
15622 if (styleProp !== undefined) {
15623 // then make a prop of it
15624 if (plainObject(styleProp)) {
15625 styleProp = this.parse(name, styleProp.strValue);
15626 } else {
15627 styleProp = this.parse(name, styleProp);
15628 }
15629 }
15630
15631 if (styleProp) {
15632 rstyle[name] = styleProp;
15633 }
15634 }
15635
15636 return rstyle;
15637};
15638
15639styfn$3.getPropsList = function (propsObj) {
15640 var self = this;
15641 var rstyle = [];
15642 var style = propsObj;
15643 var props = self.properties;
15644
15645 if (style) {
15646 var names = Object.keys(style);
15647
15648 for (var i = 0; i < names.length; i++) {
15649 var name = names[i];
15650 var val = style[name];
15651 var prop = props[name] || props[camel2dash(name)];
15652 var styleProp = this.parse(prop.name, val);
15653
15654 if (styleProp) {
15655 rstyle.push(styleProp);
15656 }
15657 }
15658 }
15659
15660 return rstyle;
15661};
15662
15663styfn$3.getNonDefaultPropertiesHash = function (ele, propNames, seed) {
15664 var hash = seed.slice();
15665 var name, val, strVal, chVal;
15666 var i, j;
15667
15668 for (i = 0; i < propNames.length; i++) {
15669 name = propNames[i];
15670 val = ele.pstyle(name, false);
15671
15672 if (val == null) {
15673 continue;
15674 } else if (val.pfValue != null) {
15675 hash[0] = hashInt(chVal, hash[0]);
15676 hash[1] = hashIntAlt(chVal, hash[1]);
15677 } else {
15678 strVal = val.strValue;
15679
15680 for (j = 0; j < strVal.length; j++) {
15681 chVal = strVal.charCodeAt(j);
15682 hash[0] = hashInt(chVal, hash[0]);
15683 hash[1] = hashIntAlt(chVal, hash[1]);
15684 }
15685 }
15686 }
15687
15688 return hash;
15689};
15690
15691styfn$3.getPropertiesHash = styfn$3.getNonDefaultPropertiesHash;
15692
15693var styfn$4 = {};
15694
15695styfn$4.appendFromJson = function (json) {
15696 var style = this;
15697
15698 for (var i = 0; i < json.length; i++) {
15699 var context = json[i];
15700 var selector = context.selector;
15701 var props = context.style || context.css;
15702 var names = Object.keys(props);
15703 style.selector(selector); // apply selector
15704
15705 for (var j = 0; j < names.length; j++) {
15706 var name = names[j];
15707 var value = props[name];
15708 style.css(name, value); // apply property
15709 }
15710 }
15711
15712 return style;
15713}; // accessible cy.style() function
15714
15715
15716styfn$4.fromJson = function (json) {
15717 var style = this;
15718 style.resetToDefault();
15719 style.appendFromJson(json);
15720 return style;
15721}; // get json from cy.style() api
15722
15723
15724styfn$4.json = function () {
15725 var json = [];
15726
15727 for (var i = this.defaultLength; i < this.length; i++) {
15728 var cxt = this[i];
15729 var selector = cxt.selector;
15730 var props = cxt.properties;
15731 var css = {};
15732
15733 for (var j = 0; j < props.length; j++) {
15734 var prop = props[j];
15735 css[prop.name] = prop.strValue;
15736 }
15737
15738 json.push({
15739 selector: !selector ? 'core' : selector.toString(),
15740 style: css
15741 });
15742 }
15743
15744 return json;
15745};
15746
15747var styfn$5 = {};
15748
15749styfn$5.appendFromString = function (string) {
15750 var self = this;
15751 var style = this;
15752 var remaining = '' + string;
15753 var selAndBlockStr;
15754 var blockRem;
15755 var propAndValStr; // remove comments from the style string
15756
15757 remaining = remaining.replace(/[/][*](\s|.)+?[*][/]/g, '');
15758
15759 function removeSelAndBlockFromRemaining() {
15760 // remove the parsed selector and block from the remaining text to parse
15761 if (remaining.length > selAndBlockStr.length) {
15762 remaining = remaining.substr(selAndBlockStr.length);
15763 } else {
15764 remaining = '';
15765 }
15766 }
15767
15768 function removePropAndValFromRem() {
15769 // remove the parsed property and value from the remaining block text to parse
15770 if (blockRem.length > propAndValStr.length) {
15771 blockRem = blockRem.substr(propAndValStr.length);
15772 } else {
15773 blockRem = '';
15774 }
15775 }
15776
15777 for (;;) {
15778 var nothingLeftToParse = remaining.match(/^\s*$/);
15779
15780 if (nothingLeftToParse) {
15781 break;
15782 }
15783
15784 var selAndBlock = remaining.match(/^\s*((?:.|\s)+?)\s*\{((?:.|\s)+?)\}/);
15785
15786 if (!selAndBlock) {
15787 warn('Halting stylesheet parsing: String stylesheet contains more to parse but no selector and block found in: ' + remaining);
15788 break;
15789 }
15790
15791 selAndBlockStr = selAndBlock[0]; // parse the selector
15792
15793 var selectorStr = selAndBlock[1];
15794
15795 if (selectorStr !== 'core') {
15796 var selector = new Selector(selectorStr);
15797
15798 if (selector.invalid) {
15799 warn('Skipping parsing of block: Invalid selector found in string stylesheet: ' + selectorStr); // skip this selector and block
15800
15801 removeSelAndBlockFromRemaining();
15802 continue;
15803 }
15804 } // parse the block of properties and values
15805
15806
15807 var blockStr = selAndBlock[2];
15808 var invalidBlock = false;
15809 blockRem = blockStr;
15810 var props = [];
15811
15812 for (;;) {
15813 var _nothingLeftToParse = blockRem.match(/^\s*$/);
15814
15815 if (_nothingLeftToParse) {
15816 break;
15817 }
15818
15819 var propAndVal = blockRem.match(/^\s*(.+?)\s*:\s*(.+?)\s*;/);
15820
15821 if (!propAndVal) {
15822 warn('Skipping parsing of block: Invalid formatting of style property and value definitions found in:' + blockStr);
15823 invalidBlock = true;
15824 break;
15825 }
15826
15827 propAndValStr = propAndVal[0];
15828 var propStr = propAndVal[1];
15829 var valStr = propAndVal[2];
15830 var prop = self.properties[propStr];
15831
15832 if (!prop) {
15833 warn('Skipping property: Invalid property name in: ' + propAndValStr); // skip this property in the block
15834
15835 removePropAndValFromRem();
15836 continue;
15837 }
15838
15839 var parsedProp = style.parse(propStr, valStr);
15840
15841 if (!parsedProp) {
15842 warn('Skipping property: Invalid property definition in: ' + propAndValStr); // skip this property in the block
15843
15844 removePropAndValFromRem();
15845 continue;
15846 }
15847
15848 props.push({
15849 name: propStr,
15850 val: valStr
15851 });
15852 removePropAndValFromRem();
15853 }
15854
15855 if (invalidBlock) {
15856 removeSelAndBlockFromRemaining();
15857 break;
15858 } // put the parsed block in the style
15859
15860
15861 style.selector(selectorStr);
15862
15863 for (var i = 0; i < props.length; i++) {
15864 var _prop = props[i];
15865 style.css(_prop.name, _prop.val);
15866 }
15867
15868 removeSelAndBlockFromRemaining();
15869 }
15870
15871 return style;
15872};
15873
15874styfn$5.fromString = function (string) {
15875 var style = this;
15876 style.resetToDefault();
15877 style.appendFromString(string);
15878 return style;
15879};
15880
15881var styfn$6 = {};
15882
15883(function () {
15884 var number = number$1;
15885 var rgba = rgbaNoBackRefs;
15886 var hsla = hslaNoBackRefs;
15887 var hex3$1 = hex3;
15888 var hex6$1 = hex6;
15889
15890 var data = function data(prefix) {
15891 return '^' + prefix + '\\s*\\(\\s*([\\w\\.]+)\\s*\\)$';
15892 };
15893
15894 var mapData = function mapData(prefix) {
15895 var mapArg = number + '|\\w+|' + rgba + '|' + hsla + '|' + hex3$1 + '|' + hex6$1;
15896 return '^' + prefix + '\\s*\\(([\\w\\.]+)\\s*\\,\\s*(' + number + ')\\s*\\,\\s*(' + number + ')\\s*,\\s*(' + mapArg + ')\\s*\\,\\s*(' + mapArg + ')\\)$';
15897 };
15898
15899 var urlRegexes = ['^url\\s*\\(\\s*[\'"]?(.+?)[\'"]?\\s*\\)$', '^(none)$', '^(.+)$']; // each visual style property has a type and needs to be validated according to it
15900
15901 styfn$6.types = {
15902 time: {
15903 number: true,
15904 min: 0,
15905 units: 's|ms',
15906 implicitUnits: 'ms'
15907 },
15908 percent: {
15909 number: true,
15910 min: 0,
15911 max: 100,
15912 units: '%',
15913 implicitUnits: '%'
15914 },
15915 percentages: {
15916 number: true,
15917 min: 0,
15918 max: 100,
15919 units: '%',
15920 implicitUnits: '%',
15921 multiple: true
15922 },
15923 zeroOneNumber: {
15924 number: true,
15925 min: 0,
15926 max: 1,
15927 unitless: true
15928 },
15929 zeroOneNumbers: {
15930 number: true,
15931 min: 0,
15932 max: 1,
15933 unitless: true,
15934 multiple: true
15935 },
15936 nOneOneNumber: {
15937 number: true,
15938 min: -1,
15939 max: 1,
15940 unitless: true
15941 },
15942 nonNegativeInt: {
15943 number: true,
15944 min: 0,
15945 integer: true,
15946 unitless: true
15947 },
15948 position: {
15949 enums: ['parent', 'origin']
15950 },
15951 nodeSize: {
15952 number: true,
15953 min: 0,
15954 enums: ['label']
15955 },
15956 number: {
15957 number: true,
15958 unitless: true
15959 },
15960 numbers: {
15961 number: true,
15962 unitless: true,
15963 multiple: true
15964 },
15965 positiveNumber: {
15966 number: true,
15967 unitless: true,
15968 min: 0,
15969 strictMin: true
15970 },
15971 size: {
15972 number: true,
15973 min: 0
15974 },
15975 bidirectionalSize: {
15976 number: true
15977 },
15978 // allows negative
15979 bidirectionalSizeMaybePercent: {
15980 number: true,
15981 allowPercent: true
15982 },
15983 // allows negative
15984 bidirectionalSizes: {
15985 number: true,
15986 multiple: true
15987 },
15988 // allows negative
15989 sizeMaybePercent: {
15990 number: true,
15991 min: 0,
15992 allowPercent: true
15993 },
15994 axisDirection: {
15995 enums: ['horizontal', 'leftward', 'rightward', 'vertical', 'upward', 'downward', 'auto']
15996 },
15997 paddingRelativeTo: {
15998 enums: ['width', 'height', 'average', 'min', 'max']
15999 },
16000 bgWH: {
16001 number: true,
16002 min: 0,
16003 allowPercent: true,
16004 enums: ['auto'],
16005 multiple: true
16006 },
16007 bgPos: {
16008 number: true,
16009 allowPercent: true,
16010 multiple: true
16011 },
16012 bgRelativeTo: {
16013 enums: ['inner', 'include-padding'],
16014 multiple: true
16015 },
16016 bgRepeat: {
16017 enums: ['repeat', 'repeat-x', 'repeat-y', 'no-repeat'],
16018 multiple: true
16019 },
16020 bgFit: {
16021 enums: ['none', 'contain', 'cover'],
16022 multiple: true
16023 },
16024 bgCrossOrigin: {
16025 enums: ['anonymous', 'use-credentials'],
16026 multiple: true
16027 },
16028 bgClip: {
16029 enums: ['none', 'node'],
16030 multiple: true
16031 },
16032 bgContainment: {
16033 enums: ['inside', 'over'],
16034 multiple: true
16035 },
16036 color: {
16037 color: true
16038 },
16039 colors: {
16040 color: true,
16041 multiple: true
16042 },
16043 fill: {
16044 enums: ['solid', 'linear-gradient', 'radial-gradient']
16045 },
16046 bool: {
16047 enums: ['yes', 'no']
16048 },
16049 bools: {
16050 enums: ['yes', 'no'],
16051 multiple: true
16052 },
16053 lineStyle: {
16054 enums: ['solid', 'dotted', 'dashed']
16055 },
16056 lineCap: {
16057 enums: ['butt', 'round', 'square']
16058 },
16059 borderStyle: {
16060 enums: ['solid', 'dotted', 'dashed', 'double']
16061 },
16062 curveStyle: {
16063 enums: ['bezier', 'unbundled-bezier', 'haystack', 'segments', 'straight', 'taxi']
16064 },
16065 fontFamily: {
16066 regex: '^([\\w- \\"]+(?:\\s*,\\s*[\\w- \\"]+)*)$'
16067 },
16068 fontStyle: {
16069 enums: ['italic', 'normal', 'oblique']
16070 },
16071 fontWeight: {
16072 enums: ['normal', 'bold', 'bolder', 'lighter', '100', '200', '300', '400', '500', '600', '800', '900', 100, 200, 300, 400, 500, 600, 700, 800, 900]
16073 },
16074 textDecoration: {
16075 enums: ['none', 'underline', 'overline', 'line-through']
16076 },
16077 textTransform: {
16078 enums: ['none', 'uppercase', 'lowercase']
16079 },
16080 textWrap: {
16081 enums: ['none', 'wrap', 'ellipsis']
16082 },
16083 textOverflowWrap: {
16084 enums: ['whitespace', 'anywhere']
16085 },
16086 textBackgroundShape: {
16087 enums: ['rectangle', 'roundrectangle', 'round-rectangle']
16088 },
16089 nodeShape: {
16090 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']
16091 },
16092 compoundIncludeLabels: {
16093 enums: ['include', 'exclude']
16094 },
16095 arrowShape: {
16096 enums: ['tee', 'triangle', 'triangle-tee', 'circle-triangle', 'triangle-cross', 'triangle-backcurve', 'vee', 'square', 'circle', 'diamond', 'chevron', 'none']
16097 },
16098 arrowFill: {
16099 enums: ['filled', 'hollow']
16100 },
16101 display: {
16102 enums: ['element', 'none']
16103 },
16104 visibility: {
16105 enums: ['hidden', 'visible']
16106 },
16107 zCompoundDepth: {
16108 enums: ['bottom', 'orphan', 'auto', 'top']
16109 },
16110 zIndexCompare: {
16111 enums: ['auto', 'manual']
16112 },
16113 valign: {
16114 enums: ['top', 'center', 'bottom']
16115 },
16116 halign: {
16117 enums: ['left', 'center', 'right']
16118 },
16119 justification: {
16120 enums: ['left', 'center', 'right', 'auto']
16121 },
16122 text: {
16123 string: true
16124 },
16125 data: {
16126 mapping: true,
16127 regex: data('data')
16128 },
16129 layoutData: {
16130 mapping: true,
16131 regex: data('layoutData')
16132 },
16133 scratch: {
16134 mapping: true,
16135 regex: data('scratch')
16136 },
16137 mapData: {
16138 mapping: true,
16139 regex: mapData('mapData')
16140 },
16141 mapLayoutData: {
16142 mapping: true,
16143 regex: mapData('mapLayoutData')
16144 },
16145 mapScratch: {
16146 mapping: true,
16147 regex: mapData('mapScratch')
16148 },
16149 fn: {
16150 mapping: true,
16151 fn: true
16152 },
16153 url: {
16154 regexes: urlRegexes,
16155 singleRegexMatchValue: true
16156 },
16157 urls: {
16158 regexes: urlRegexes,
16159 singleRegexMatchValue: true,
16160 multiple: true
16161 },
16162 propList: {
16163 propList: true
16164 },
16165 angle: {
16166 number: true,
16167 units: 'deg|rad',
16168 implicitUnits: 'rad'
16169 },
16170 textRotation: {
16171 number: true,
16172 units: 'deg|rad',
16173 implicitUnits: 'rad',
16174 enums: ['none', 'autorotate']
16175 },
16176 polygonPointList: {
16177 number: true,
16178 multiple: true,
16179 evenMultiple: true,
16180 min: -1,
16181 max: 1,
16182 unitless: true
16183 },
16184 edgeDistances: {
16185 enums: ['intersection', 'node-position']
16186 },
16187 edgeEndpoint: {
16188 number: true,
16189 multiple: true,
16190 units: '%|px|em|deg|rad',
16191 implicitUnits: 'px',
16192 enums: ['inside-to-node', 'outside-to-node', 'outside-to-node-or-label', 'outside-to-line', 'outside-to-line-or-label'],
16193 singleEnum: true,
16194 validate: function validate(valArr, unitsArr) {
16195 switch (valArr.length) {
16196 case 2:
16197 // can be % or px only
16198 return unitsArr[0] !== 'deg' && unitsArr[0] !== 'rad' && unitsArr[1] !== 'deg' && unitsArr[1] !== 'rad';
16199
16200 case 1:
16201 // can be enum, deg, or rad only
16202 return string(valArr[0]) || unitsArr[0] === 'deg' || unitsArr[0] === 'rad';
16203
16204 default:
16205 return false;
16206 }
16207 }
16208 },
16209 easing: {
16210 regexes: ['^(spring)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$', '^(cubic-bezier)\\s*\\(\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*,\\s*(' + number + ')\\s*\\)$'],
16211 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']
16212 },
16213 gradientDirection: {
16214 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']
16215 },
16216 boundsExpansion: {
16217 number: true,
16218 multiple: true,
16219 min: 0,
16220 validate: function validate(valArr) {
16221 var length = valArr.length;
16222 return length === 1 || length === 2 || length === 4;
16223 }
16224 }
16225 };
16226 var diff = {
16227 zeroNonZero: function zeroNonZero(val1, val2) {
16228 if ((val1 == null || val2 == null) && val1 !== val2) {
16229 return true; // null cases could represent any value
16230 }
16231
16232 if (val1 == 0 && val2 != 0) {
16233 return true;
16234 } else if (val1 != 0 && val2 == 0) {
16235 return true;
16236 } else {
16237 return false;
16238 }
16239 },
16240 any: function any(val1, val2) {
16241 return val1 != val2;
16242 },
16243 emptyNonEmpty: function emptyNonEmpty(str1, str2) {
16244 var empty1 = emptyString(str1);
16245 var empty2 = emptyString(str2);
16246 return empty1 && !empty2 || !empty1 && empty2;
16247 }
16248 }; // define visual style properties
16249 //
16250 // - n.b. adding a new group of props may require updates to updateStyleHints()
16251 // - adding new props to an existing group gets handled automatically
16252
16253 var t = styfn$6.types;
16254 var mainLabel = [{
16255 name: 'label',
16256 type: t.text,
16257 triggersBounds: diff.any,
16258 triggersZOrder: diff.emptyNonEmpty
16259 }, {
16260 name: 'text-rotation',
16261 type: t.textRotation,
16262 triggersBounds: diff.any
16263 }, {
16264 name: 'text-margin-x',
16265 type: t.bidirectionalSize,
16266 triggersBounds: diff.any
16267 }, {
16268 name: 'text-margin-y',
16269 type: t.bidirectionalSize,
16270 triggersBounds: diff.any
16271 }];
16272 var sourceLabel = [{
16273 name: 'source-label',
16274 type: t.text,
16275 triggersBounds: diff.any
16276 }, {
16277 name: 'source-text-rotation',
16278 type: t.textRotation,
16279 triggersBounds: diff.any
16280 }, {
16281 name: 'source-text-margin-x',
16282 type: t.bidirectionalSize,
16283 triggersBounds: diff.any
16284 }, {
16285 name: 'source-text-margin-y',
16286 type: t.bidirectionalSize,
16287 triggersBounds: diff.any
16288 }, {
16289 name: 'source-text-offset',
16290 type: t.size,
16291 triggersBounds: diff.any
16292 }];
16293 var targetLabel = [{
16294 name: 'target-label',
16295 type: t.text,
16296 triggersBounds: diff.any
16297 }, {
16298 name: 'target-text-rotation',
16299 type: t.textRotation,
16300 triggersBounds: diff.any
16301 }, {
16302 name: 'target-text-margin-x',
16303 type: t.bidirectionalSize,
16304 triggersBounds: diff.any
16305 }, {
16306 name: 'target-text-margin-y',
16307 type: t.bidirectionalSize,
16308 triggersBounds: diff.any
16309 }, {
16310 name: 'target-text-offset',
16311 type: t.size,
16312 triggersBounds: diff.any
16313 }];
16314 var labelDimensions = [{
16315 name: 'font-family',
16316 type: t.fontFamily,
16317 triggersBounds: diff.any
16318 }, {
16319 name: 'font-style',
16320 type: t.fontStyle,
16321 triggersBounds: diff.any
16322 }, {
16323 name: 'font-weight',
16324 type: t.fontWeight,
16325 triggersBounds: diff.any
16326 }, {
16327 name: 'font-size',
16328 type: t.size,
16329 triggersBounds: diff.any
16330 }, {
16331 name: 'text-transform',
16332 type: t.textTransform,
16333 triggersBounds: diff.any
16334 }, {
16335 name: 'text-wrap',
16336 type: t.textWrap,
16337 triggersBounds: diff.any
16338 }, {
16339 name: 'text-overflow-wrap',
16340 type: t.textOverflowWrap,
16341 triggersBounds: diff.any
16342 }, {
16343 name: 'text-max-width',
16344 type: t.size,
16345 triggersBounds: diff.any
16346 }, {
16347 name: 'text-outline-width',
16348 type: t.size,
16349 triggersBounds: diff.any
16350 }, {
16351 name: 'line-height',
16352 type: t.positiveNumber,
16353 triggersBounds: diff.any
16354 }];
16355 var commonLabel = [{
16356 name: 'text-valign',
16357 type: t.valign,
16358 triggersBounds: diff.any
16359 }, {
16360 name: 'text-halign',
16361 type: t.halign,
16362 triggersBounds: diff.any
16363 }, {
16364 name: 'color',
16365 type: t.color
16366 }, {
16367 name: 'text-outline-color',
16368 type: t.color
16369 }, {
16370 name: 'text-outline-opacity',
16371 type: t.zeroOneNumber
16372 }, {
16373 name: 'text-background-color',
16374 type: t.color
16375 }, {
16376 name: 'text-background-opacity',
16377 type: t.zeroOneNumber
16378 }, {
16379 name: 'text-background-padding',
16380 type: t.size,
16381 triggersBounds: diff.any
16382 }, {
16383 name: 'text-border-opacity',
16384 type: t.zeroOneNumber
16385 }, {
16386 name: 'text-border-color',
16387 type: t.color
16388 }, {
16389 name: 'text-border-width',
16390 type: t.size,
16391 triggersBounds: diff.any
16392 }, {
16393 name: 'text-border-style',
16394 type: t.borderStyle,
16395 triggersBounds: diff.any
16396 }, {
16397 name: 'text-background-shape',
16398 type: t.textBackgroundShape,
16399 triggersBounds: diff.any
16400 }, {
16401 name: 'text-justification',
16402 type: t.justification
16403 }];
16404 var behavior = [{
16405 name: 'events',
16406 type: t.bool
16407 }, {
16408 name: 'text-events',
16409 type: t.bool
16410 }];
16411 var visibility = [{
16412 name: 'display',
16413 type: t.display,
16414 triggersZOrder: diff.any,
16415 triggersBounds: diff.any,
16416 triggersBoundsOfParallelBeziers: true
16417 }, {
16418 name: 'visibility',
16419 type: t.visibility,
16420 triggersZOrder: diff.any
16421 }, {
16422 name: 'opacity',
16423 type: t.zeroOneNumber,
16424 triggersZOrder: diff.zeroNonZero
16425 }, {
16426 name: 'text-opacity',
16427 type: t.zeroOneNumber
16428 }, {
16429 name: 'min-zoomed-font-size',
16430 type: t.size
16431 }, {
16432 name: 'z-compound-depth',
16433 type: t.zCompoundDepth,
16434 triggersZOrder: diff.any
16435 }, {
16436 name: 'z-index-compare',
16437 type: t.zIndexCompare,
16438 triggersZOrder: diff.any
16439 }, {
16440 name: 'z-index',
16441 type: t.nonNegativeInt,
16442 triggersZOrder: diff.any
16443 }];
16444 var overlay = [{
16445 name: 'overlay-padding',
16446 type: t.size,
16447 triggersBounds: diff.any
16448 }, {
16449 name: 'overlay-color',
16450 type: t.color
16451 }, {
16452 name: 'overlay-opacity',
16453 type: t.zeroOneNumber,
16454 triggersBounds: diff.zeroNonZero
16455 }];
16456 var transition = [{
16457 name: 'transition-property',
16458 type: t.propList
16459 }, {
16460 name: 'transition-duration',
16461 type: t.time
16462 }, {
16463 name: 'transition-delay',
16464 type: t.time
16465 }, {
16466 name: 'transition-timing-function',
16467 type: t.easing
16468 }];
16469
16470 var nodeSizeHashOverride = function nodeSizeHashOverride(ele, parsedProp) {
16471 if (parsedProp.value === 'label') {
16472 return -ele.poolIndex(); // no hash key hits is using label size (hitrate for perf probably low anyway)
16473 } else {
16474 return parsedProp.pfValue;
16475 }
16476 };
16477
16478 var nodeBody = [{
16479 name: 'height',
16480 type: t.nodeSize,
16481 triggersBounds: diff.any,
16482 hashOverride: nodeSizeHashOverride
16483 }, {
16484 name: 'width',
16485 type: t.nodeSize,
16486 triggersBounds: diff.any,
16487 hashOverride: nodeSizeHashOverride
16488 }, {
16489 name: 'shape',
16490 type: t.nodeShape,
16491 triggersBounds: diff.any
16492 }, {
16493 name: 'shape-polygon-points',
16494 type: t.polygonPointList,
16495 triggersBounds: diff.any
16496 }, {
16497 name: 'background-color',
16498 type: t.color
16499 }, {
16500 name: 'background-fill',
16501 type: t.fill
16502 }, {
16503 name: 'background-opacity',
16504 type: t.zeroOneNumber
16505 }, {
16506 name: 'background-blacken',
16507 type: t.nOneOneNumber
16508 }, {
16509 name: 'background-gradient-stop-colors',
16510 type: t.colors
16511 }, {
16512 name: 'background-gradient-stop-positions',
16513 type: t.percentages
16514 }, {
16515 name: 'background-gradient-direction',
16516 type: t.gradientDirection
16517 }, {
16518 name: 'padding',
16519 type: t.sizeMaybePercent,
16520 triggersBounds: diff.any
16521 }, {
16522 name: 'padding-relative-to',
16523 type: t.paddingRelativeTo,
16524 triggersBounds: diff.any
16525 }, {
16526 name: 'bounds-expansion',
16527 type: t.boundsExpansion,
16528 triggersBounds: diff.any
16529 }];
16530 var nodeBorder = [{
16531 name: 'border-color',
16532 type: t.color
16533 }, {
16534 name: 'border-opacity',
16535 type: t.zeroOneNumber
16536 }, {
16537 name: 'border-width',
16538 type: t.size,
16539 triggersBounds: diff.any
16540 }, {
16541 name: 'border-style',
16542 type: t.borderStyle
16543 }];
16544 var backgroundImage = [{
16545 name: 'background-image',
16546 type: t.urls
16547 }, {
16548 name: 'background-image-crossorigin',
16549 type: t.bgCrossOrigin
16550 }, {
16551 name: 'background-image-opacity',
16552 type: t.zeroOneNumbers
16553 }, {
16554 name: 'background-image-containment',
16555 type: t.bgContainment
16556 }, {
16557 name: 'background-image-smoothing',
16558 type: t.bools
16559 }, {
16560 name: 'background-position-x',
16561 type: t.bgPos
16562 }, {
16563 name: 'background-position-y',
16564 type: t.bgPos
16565 }, {
16566 name: 'background-width-relative-to',
16567 type: t.bgRelativeTo
16568 }, {
16569 name: 'background-height-relative-to',
16570 type: t.bgRelativeTo
16571 }, {
16572 name: 'background-repeat',
16573 type: t.bgRepeat
16574 }, {
16575 name: 'background-fit',
16576 type: t.bgFit
16577 }, {
16578 name: 'background-clip',
16579 type: t.bgClip
16580 }, {
16581 name: 'background-width',
16582 type: t.bgWH
16583 }, {
16584 name: 'background-height',
16585 type: t.bgWH
16586 }, {
16587 name: 'background-offset-x',
16588 type: t.bgPos
16589 }, {
16590 name: 'background-offset-y',
16591 type: t.bgPos
16592 }];
16593 var compound = [{
16594 name: 'position',
16595 type: t.position,
16596 triggersBounds: diff.any
16597 }, {
16598 name: 'compound-sizing-wrt-labels',
16599 type: t.compoundIncludeLabels,
16600 triggersBounds: diff.any
16601 }, {
16602 name: 'min-width',
16603 type: t.size,
16604 triggersBounds: diff.any
16605 }, {
16606 name: 'min-width-bias-left',
16607 type: t.sizeMaybePercent,
16608 triggersBounds: diff.any
16609 }, {
16610 name: 'min-width-bias-right',
16611 type: t.sizeMaybePercent,
16612 triggersBounds: diff.any
16613 }, {
16614 name: 'min-height',
16615 type: t.size,
16616 triggersBounds: diff.any
16617 }, {
16618 name: 'min-height-bias-top',
16619 type: t.sizeMaybePercent,
16620 triggersBounds: diff.any
16621 }, {
16622 name: 'min-height-bias-bottom',
16623 type: t.sizeMaybePercent,
16624 triggersBounds: diff.any
16625 }];
16626 var edgeLine = [{
16627 name: 'line-style',
16628 type: t.lineStyle
16629 }, {
16630 name: 'line-color',
16631 type: t.color
16632 }, {
16633 name: 'line-fill',
16634 type: t.fill
16635 }, {
16636 name: 'line-cap',
16637 type: t.lineCap
16638 }, {
16639 name: 'line-opacity',
16640 type: t.zeroOneNumber
16641 }, {
16642 name: 'line-dash-pattern',
16643 type: t.numbers
16644 }, {
16645 name: 'line-dash-offset',
16646 type: t.number
16647 }, {
16648 name: 'line-gradient-stop-colors',
16649 type: t.colors
16650 }, {
16651 name: 'line-gradient-stop-positions',
16652 type: t.percentages
16653 }, {
16654 name: 'curve-style',
16655 type: t.curveStyle,
16656 triggersBounds: diff.any,
16657 triggersBoundsOfParallelBeziers: true
16658 }, {
16659 name: 'haystack-radius',
16660 type: t.zeroOneNumber,
16661 triggersBounds: diff.any
16662 }, {
16663 name: 'source-endpoint',
16664 type: t.edgeEndpoint,
16665 triggersBounds: diff.any
16666 }, {
16667 name: 'target-endpoint',
16668 type: t.edgeEndpoint,
16669 triggersBounds: diff.any
16670 }, {
16671 name: 'control-point-step-size',
16672 type: t.size,
16673 triggersBounds: diff.any
16674 }, {
16675 name: 'control-point-distances',
16676 type: t.bidirectionalSizes,
16677 triggersBounds: diff.any
16678 }, {
16679 name: 'control-point-weights',
16680 type: t.numbers,
16681 triggersBounds: diff.any
16682 }, {
16683 name: 'segment-distances',
16684 type: t.bidirectionalSizes,
16685 triggersBounds: diff.any
16686 }, {
16687 name: 'segment-weights',
16688 type: t.numbers,
16689 triggersBounds: diff.any
16690 }, {
16691 name: 'taxi-turn',
16692 type: t.bidirectionalSizeMaybePercent,
16693 triggersBounds: diff.any
16694 }, {
16695 name: 'taxi-turn-min-distance',
16696 type: t.size,
16697 triggersBounds: diff.any
16698 }, {
16699 name: 'taxi-direction',
16700 type: t.axisDirection,
16701 triggersBounds: diff.any
16702 }, {
16703 name: 'edge-distances',
16704 type: t.edgeDistances,
16705 triggersBounds: diff.any
16706 }, {
16707 name: 'arrow-scale',
16708 type: t.positiveNumber,
16709 triggersBounds: diff.any
16710 }, {
16711 name: 'loop-direction',
16712 type: t.angle,
16713 triggersBounds: diff.any
16714 }, {
16715 name: 'loop-sweep',
16716 type: t.angle,
16717 triggersBounds: diff.any
16718 }, {
16719 name: 'source-distance-from-node',
16720 type: t.size,
16721 triggersBounds: diff.any
16722 }, {
16723 name: 'target-distance-from-node',
16724 type: t.size,
16725 triggersBounds: diff.any
16726 }];
16727 var ghost = [{
16728 name: 'ghost',
16729 type: t.bool,
16730 triggersBounds: diff.any
16731 }, {
16732 name: 'ghost-offset-x',
16733 type: t.bidirectionalSize,
16734 triggersBounds: diff.any
16735 }, {
16736 name: 'ghost-offset-y',
16737 type: t.bidirectionalSize,
16738 triggersBounds: diff.any
16739 }, {
16740 name: 'ghost-opacity',
16741 type: t.zeroOneNumber
16742 }];
16743 var core = [{
16744 name: 'selection-box-color',
16745 type: t.color
16746 }, {
16747 name: 'selection-box-opacity',
16748 type: t.zeroOneNumber
16749 }, {
16750 name: 'selection-box-border-color',
16751 type: t.color
16752 }, {
16753 name: 'selection-box-border-width',
16754 type: t.size
16755 }, {
16756 name: 'active-bg-color',
16757 type: t.color
16758 }, {
16759 name: 'active-bg-opacity',
16760 type: t.zeroOneNumber
16761 }, {
16762 name: 'active-bg-size',
16763 type: t.size
16764 }, {
16765 name: 'outside-texture-bg-color',
16766 type: t.color
16767 }, {
16768 name: 'outside-texture-bg-opacity',
16769 type: t.zeroOneNumber
16770 }]; // pie backgrounds for nodes
16771
16772 var pie = [];
16773 styfn$6.pieBackgroundN = 16; // because the pie properties are numbered, give access to a constant N (for renderer use)
16774
16775 pie.push({
16776 name: 'pie-size',
16777 type: t.sizeMaybePercent
16778 });
16779
16780 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
16781 pie.push({
16782 name: 'pie-' + i + '-background-color',
16783 type: t.color
16784 });
16785 pie.push({
16786 name: 'pie-' + i + '-background-size',
16787 type: t.percent
16788 });
16789 pie.push({
16790 name: 'pie-' + i + '-background-opacity',
16791 type: t.zeroOneNumber
16792 });
16793 } // edge arrows
16794
16795
16796 var edgeArrow = [];
16797 var arrowPrefixes = styfn$6.arrowPrefixes = ['source', 'mid-source', 'target', 'mid-target'];
16798 [{
16799 name: 'arrow-shape',
16800 type: t.arrowShape,
16801 triggersBounds: diff.any
16802 }, {
16803 name: 'arrow-color',
16804 type: t.color
16805 }, {
16806 name: 'arrow-fill',
16807 type: t.arrowFill
16808 }].forEach(function (prop) {
16809 arrowPrefixes.forEach(function (prefix) {
16810 var name = prefix + '-' + prop.name;
16811 var type = prop.type,
16812 triggersBounds = prop.triggersBounds;
16813 edgeArrow.push({
16814 name: name,
16815 type: type,
16816 triggersBounds: triggersBounds
16817 });
16818 });
16819 }, {});
16820 var props = styfn$6.properties = [].concat(behavior, transition, visibility, overlay, ghost, commonLabel, labelDimensions, mainLabel, sourceLabel, targetLabel, nodeBody, nodeBorder, backgroundImage, pie, compound, edgeLine, edgeArrow, core);
16821 var propGroups = styfn$6.propertyGroups = {
16822 // common to all eles
16823 behavior: behavior,
16824 transition: transition,
16825 visibility: visibility,
16826 overlay: overlay,
16827 ghost: ghost,
16828 // labels
16829 commonLabel: commonLabel,
16830 labelDimensions: labelDimensions,
16831 mainLabel: mainLabel,
16832 sourceLabel: sourceLabel,
16833 targetLabel: targetLabel,
16834 // node props
16835 nodeBody: nodeBody,
16836 nodeBorder: nodeBorder,
16837 backgroundImage: backgroundImage,
16838 pie: pie,
16839 compound: compound,
16840 // edge props
16841 edgeLine: edgeLine,
16842 edgeArrow: edgeArrow,
16843 core: core
16844 };
16845 var propGroupNames = styfn$6.propertyGroupNames = {};
16846 var propGroupKeys = styfn$6.propertyGroupKeys = Object.keys(propGroups);
16847 propGroupKeys.forEach(function (key) {
16848 propGroupNames[key] = propGroups[key].map(function (prop) {
16849 return prop.name;
16850 });
16851 propGroups[key].forEach(function (prop) {
16852 return prop.groupKey = key;
16853 });
16854 }); // define aliases
16855
16856 var aliases = styfn$6.aliases = [{
16857 name: 'content',
16858 pointsTo: 'label'
16859 }, {
16860 name: 'control-point-distance',
16861 pointsTo: 'control-point-distances'
16862 }, {
16863 name: 'control-point-weight',
16864 pointsTo: 'control-point-weights'
16865 }, {
16866 name: 'edge-text-rotation',
16867 pointsTo: 'text-rotation'
16868 }, {
16869 name: 'padding-left',
16870 pointsTo: 'padding'
16871 }, {
16872 name: 'padding-right',
16873 pointsTo: 'padding'
16874 }, {
16875 name: 'padding-top',
16876 pointsTo: 'padding'
16877 }, {
16878 name: 'padding-bottom',
16879 pointsTo: 'padding'
16880 }]; // list of property names
16881
16882 styfn$6.propertyNames = props.map(function (p) {
16883 return p.name;
16884 }); // allow access of properties by name ( e.g. style.properties.height )
16885
16886 for (var _i = 0; _i < props.length; _i++) {
16887 var prop = props[_i];
16888 props[prop.name] = prop; // allow lookup by name
16889 } // map aliases
16890
16891
16892 for (var _i2 = 0; _i2 < aliases.length; _i2++) {
16893 var alias = aliases[_i2];
16894 var pointsToProp = props[alias.pointsTo];
16895 var aliasProp = {
16896 name: alias.name,
16897 alias: true,
16898 pointsTo: pointsToProp
16899 }; // add alias prop for parsing
16900
16901 props.push(aliasProp);
16902 props[alias.name] = aliasProp; // allow lookup by name
16903 }
16904})();
16905
16906styfn$6.getDefaultProperty = function (name) {
16907 return this.getDefaultProperties()[name];
16908};
16909
16910styfn$6.getDefaultProperties = function () {
16911 var _p = this._private;
16912
16913 if (_p.defaultProperties != null) {
16914 return _p.defaultProperties;
16915 }
16916
16917 var rawProps = extend({
16918 // core props
16919 'selection-box-color': '#ddd',
16920 'selection-box-opacity': 0.65,
16921 'selection-box-border-color': '#aaa',
16922 'selection-box-border-width': 1,
16923 'active-bg-color': 'black',
16924 'active-bg-opacity': 0.15,
16925 'active-bg-size': 30,
16926 'outside-texture-bg-color': '#000',
16927 'outside-texture-bg-opacity': 0.125,
16928 // common node/edge props
16929 'events': 'yes',
16930 'text-events': 'no',
16931 'text-valign': 'top',
16932 'text-halign': 'center',
16933 'text-justification': 'auto',
16934 'line-height': 1,
16935 'color': '#000',
16936 'text-outline-color': '#000',
16937 'text-outline-width': 0,
16938 'text-outline-opacity': 1,
16939 'text-opacity': 1,
16940 'text-decoration': 'none',
16941 'text-transform': 'none',
16942 'text-wrap': 'none',
16943 'text-overflow-wrap': 'whitespace',
16944 'text-max-width': 9999,
16945 'text-background-color': '#000',
16946 'text-background-opacity': 0,
16947 'text-background-shape': 'rectangle',
16948 'text-background-padding': 0,
16949 'text-border-opacity': 0,
16950 'text-border-width': 0,
16951 'text-border-style': 'solid',
16952 'text-border-color': '#000',
16953 'font-family': 'Helvetica Neue, Helvetica, sans-serif',
16954 'font-style': 'normal',
16955 'font-weight': 'normal',
16956 'font-size': 16,
16957 'min-zoomed-font-size': 0,
16958 'text-rotation': 'none',
16959 'source-text-rotation': 'none',
16960 'target-text-rotation': 'none',
16961 'visibility': 'visible',
16962 'display': 'element',
16963 'opacity': 1,
16964 'z-compound-depth': 'auto',
16965 'z-index-compare': 'auto',
16966 'z-index': 0,
16967 'label': '',
16968 'text-margin-x': 0,
16969 'text-margin-y': 0,
16970 'source-label': '',
16971 'source-text-offset': 0,
16972 'source-text-margin-x': 0,
16973 'source-text-margin-y': 0,
16974 'target-label': '',
16975 'target-text-offset': 0,
16976 'target-text-margin-x': 0,
16977 'target-text-margin-y': 0,
16978 'overlay-opacity': 0,
16979 'overlay-color': '#000',
16980 'overlay-padding': 10,
16981 'transition-property': 'none',
16982 'transition-duration': 0,
16983 'transition-delay': 0,
16984 'transition-timing-function': 'linear',
16985 // node props
16986 'background-blacken': 0,
16987 'background-color': '#999',
16988 'background-fill': 'solid',
16989 'background-opacity': 1,
16990 'background-image': 'none',
16991 'background-image-crossorigin': 'anonymous',
16992 'background-image-opacity': 1,
16993 'background-image-containment': 'inside',
16994 'background-image-smoothing': 'yes',
16995 'background-position-x': '50%',
16996 'background-position-y': '50%',
16997 'background-offset-x': 0,
16998 'background-offset-y': 0,
16999 'background-width-relative-to': 'include-padding',
17000 'background-height-relative-to': 'include-padding',
17001 'background-repeat': 'no-repeat',
17002 'background-fit': 'none',
17003 'background-clip': 'node',
17004 'background-width': 'auto',
17005 'background-height': 'auto',
17006 'border-color': '#000',
17007 'border-opacity': 1,
17008 'border-width': 0,
17009 'border-style': 'solid',
17010 'height': 30,
17011 'width': 30,
17012 'shape': 'ellipse',
17013 'shape-polygon-points': '-1, -1, 1, -1, 1, 1, -1, 1',
17014 'bounds-expansion': 0,
17015 // node gradient
17016 'background-gradient-direction': 'to-bottom',
17017 'background-gradient-stop-colors': '#999',
17018 'background-gradient-stop-positions': '0%',
17019 // ghost props
17020 'ghost': 'no',
17021 'ghost-offset-y': 0,
17022 'ghost-offset-x': 0,
17023 'ghost-opacity': 0,
17024 // compound props
17025 'padding': 0,
17026 'padding-relative-to': 'width',
17027 'position': 'origin',
17028 'compound-sizing-wrt-labels': 'include',
17029 'min-width': 0,
17030 'min-width-bias-left': 0,
17031 'min-width-bias-right': 0,
17032 'min-height': 0,
17033 'min-height-bias-top': 0,
17034 'min-height-bias-bottom': 0
17035 }, {
17036 // node pie bg
17037 'pie-size': '100%'
17038 }, [{
17039 name: 'pie-{{i}}-background-color',
17040 value: 'black'
17041 }, {
17042 name: 'pie-{{i}}-background-size',
17043 value: '0%'
17044 }, {
17045 name: 'pie-{{i}}-background-opacity',
17046 value: 1
17047 }].reduce(function (css, prop) {
17048 for (var i = 1; i <= styfn$6.pieBackgroundN; i++) {
17049 var name = prop.name.replace('{{i}}', i);
17050 var val = prop.value;
17051 css[name] = val;
17052 }
17053
17054 return css;
17055 }, {}), {
17056 // edge props
17057 'line-style': 'solid',
17058 'line-color': '#999',
17059 'line-fill': 'solid',
17060 'line-cap': 'butt',
17061 'line-opacity': 1,
17062 'line-gradient-stop-colors': '#999',
17063 'line-gradient-stop-positions': '0%',
17064 'control-point-step-size': 40,
17065 'control-point-weights': 0.5,
17066 'segment-weights': 0.5,
17067 'segment-distances': 20,
17068 'taxi-turn': '50%',
17069 'taxi-turn-min-distance': 10,
17070 'taxi-direction': 'auto',
17071 'edge-distances': 'intersection',
17072 'curve-style': 'haystack',
17073 'haystack-radius': 0,
17074 'arrow-scale': 1,
17075 'loop-direction': '-45deg',
17076 'loop-sweep': '-90deg',
17077 'source-distance-from-node': 0,
17078 'target-distance-from-node': 0,
17079 'source-endpoint': 'outside-to-node',
17080 'target-endpoint': 'outside-to-node',
17081 'line-dash-pattern': [6, 3],
17082 'line-dash-offset': 0
17083 }, [{
17084 name: 'arrow-shape',
17085 value: 'none'
17086 }, {
17087 name: 'arrow-color',
17088 value: '#999'
17089 }, {
17090 name: 'arrow-fill',
17091 value: 'filled'
17092 }].reduce(function (css, prop) {
17093 styfn$6.arrowPrefixes.forEach(function (prefix) {
17094 var name = prefix + '-' + prop.name;
17095 var val = prop.value;
17096 css[name] = val;
17097 });
17098 return css;
17099 }, {}));
17100 var parsedProps = {};
17101
17102 for (var i = 0; i < this.properties.length; i++) {
17103 var prop = this.properties[i];
17104
17105 if (prop.pointsTo) {
17106 continue;
17107 }
17108
17109 var name = prop.name;
17110 var val = rawProps[name];
17111 var parsedProp = this.parse(name, val);
17112 parsedProps[name] = parsedProp;
17113 }
17114
17115 _p.defaultProperties = parsedProps;
17116 return _p.defaultProperties;
17117};
17118
17119styfn$6.addDefaultStylesheet = function () {
17120 this.selector(':parent').css({
17121 'shape': 'rectangle',
17122 'padding': 10,
17123 'background-color': '#eee',
17124 'border-color': '#ccc',
17125 'border-width': 1
17126 }).selector('edge').css({
17127 'width': 3
17128 }).selector(':loop').css({
17129 'curve-style': 'bezier'
17130 }).selector('edge:compound').css({
17131 'curve-style': 'bezier',
17132 'source-endpoint': 'outside-to-line',
17133 'target-endpoint': 'outside-to-line'
17134 }).selector(':selected').css({
17135 'background-color': '#0169D9',
17136 'line-color': '#0169D9',
17137 'source-arrow-color': '#0169D9',
17138 'target-arrow-color': '#0169D9',
17139 'mid-source-arrow-color': '#0169D9',
17140 'mid-target-arrow-color': '#0169D9'
17141 }).selector(':parent:selected').css({
17142 'background-color': '#CCE1F9',
17143 'border-color': '#aec8e5'
17144 }).selector(':active').css({
17145 'overlay-color': 'black',
17146 'overlay-padding': 10,
17147 'overlay-opacity': 0.25
17148 });
17149 this.defaultLength = this.length;
17150};
17151
17152var styfn$7 = {}; // a caching layer for property parsing
17153
17154styfn$7.parse = function (name, value, propIsBypass, propIsFlat) {
17155 var self = this; // function values can't be cached in all cases, and there isn't much benefit of caching them anyway
17156
17157 if (fn(value)) {
17158 return self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17159 }
17160
17161 var flatKey = propIsFlat === 'mapping' || propIsFlat === true || propIsFlat === false || propIsFlat == null ? 'dontcare' : propIsFlat;
17162 var bypassKey = propIsBypass ? 't' : 'f';
17163 var valueKey = '' + value;
17164 var argHash = hashStrings(name, valueKey, bypassKey, flatKey);
17165 var propCache = self.propCache = self.propCache || [];
17166 var ret;
17167
17168 if (!(ret = propCache[argHash])) {
17169 ret = propCache[argHash] = self.parseImplWarn(name, value, propIsBypass, propIsFlat);
17170 } // - bypasses can't be shared b/c the value can be changed by animations or otherwise overridden
17171 // - mappings can't be shared b/c mappings are per-element
17172
17173
17174 if (propIsBypass || propIsFlat === 'mapping') {
17175 // need a copy since props are mutated later in their lifecycles
17176 ret = copy(ret);
17177
17178 if (ret) {
17179 ret.value = copy(ret.value); // because it could be an array, e.g. colour
17180 }
17181 }
17182
17183 return ret;
17184};
17185
17186styfn$7.parseImplWarn = function (name, value, propIsBypass, propIsFlat) {
17187 var prop = this.parseImpl(name, value, propIsBypass, propIsFlat);
17188
17189 if (!prop && value != null) {
17190 warn("The style property `".concat(name, ": ").concat(value, "` is invalid"));
17191 }
17192
17193 if (prop && (prop.name === 'width' || prop.name === 'height') && value === 'label') {
17194 warn('The style value of `label` is deprecated for `' + prop.name + '`');
17195 }
17196
17197 return prop;
17198}; // parse a property; return null on invalid; return parsed property otherwise
17199// fields :
17200// - name : the name of the property
17201// - value : the parsed, native-typed value of the property
17202// - strValue : a string value that represents the property value in valid css
17203// - bypass : true iff the property is a bypass property
17204
17205
17206styfn$7.parseImpl = function (name, value, propIsBypass, propIsFlat) {
17207 var self = this;
17208 name = camel2dash(name); // make sure the property name is in dash form (e.g. 'property-name' not 'propertyName')
17209
17210 var property = self.properties[name];
17211 var passedValue = value;
17212 var types = self.types;
17213
17214 if (!property) {
17215 return null;
17216 } // return null on property of unknown name
17217
17218
17219 if (value === undefined) {
17220 return null;
17221 } // can't assign undefined
17222 // the property may be an alias
17223
17224
17225 if (property.alias) {
17226 property = property.pointsTo;
17227 name = property.name;
17228 }
17229
17230 var valueIsString = string(value);
17231
17232 if (valueIsString) {
17233 // trim the value to make parsing easier
17234 value = value.trim();
17235 }
17236
17237 var type = property.type;
17238
17239 if (!type) {
17240 return null;
17241 } // no type, no luck
17242 // check if bypass is null or empty string (i.e. indication to delete bypass property)
17243
17244
17245 if (propIsBypass && (value === '' || value === null)) {
17246 return {
17247 name: name,
17248 value: value,
17249 bypass: true,
17250 deleteBypass: true
17251 };
17252 } // check if value is a function used as a mapper
17253
17254
17255 if (fn(value)) {
17256 return {
17257 name: name,
17258 value: value,
17259 strValue: 'fn',
17260 mapped: types.fn,
17261 bypass: propIsBypass
17262 };
17263 } // check if value is mapped
17264
17265
17266 var data, mapData;
17267
17268 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))) {
17269 if (propIsBypass) {
17270 return false;
17271 } // mappers not allowed in bypass
17272
17273
17274 var mapped = types.data;
17275 return {
17276 name: name,
17277 value: data,
17278 strValue: '' + value,
17279 mapped: mapped,
17280 field: data[1],
17281 bypass: propIsBypass
17282 };
17283 } else if (value.length >= 10 && value[0] === 'm' && (mapData = new RegExp(types.mapData.regex).exec(value))) {
17284 if (propIsBypass) {
17285 return false;
17286 } // mappers not allowed in bypass
17287
17288
17289 if (type.multiple) {
17290 return false;
17291 } // impossible to map to num
17292
17293
17294 var _mapped = types.mapData; // we can map only if the type is a colour or a number
17295
17296 if (!(type.color || type.number)) {
17297 return false;
17298 }
17299
17300 var valueMin = this.parse(name, mapData[4]); // parse to validate
17301
17302 if (!valueMin || valueMin.mapped) {
17303 return false;
17304 } // can't be invalid or mapped
17305
17306
17307 var valueMax = this.parse(name, mapData[5]); // parse to validate
17308
17309 if (!valueMax || valueMax.mapped) {
17310 return false;
17311 } // can't be invalid or mapped
17312 // check if valueMin and valueMax are the same
17313
17314
17315 if (valueMin.pfValue === valueMax.pfValue || valueMin.strValue === valueMax.strValue) {
17316 warn('`' + name + ': ' + value + '` is not a valid mapper because the output range is zero; converting to `' + name + ': ' + valueMin.strValue + '`');
17317 return this.parse(name, valueMin.strValue); // can't make much of a mapper without a range
17318 } else if (type.color) {
17319 var c1 = valueMin.value;
17320 var c2 = valueMax.value;
17321 var same = c1[0] === c2[0] // red
17322 && c1[1] === c2[1] // green
17323 && c1[2] === c2[2] // blue
17324 && ( // optional alpha
17325 c1[3] === c2[3] // same alpha outright
17326 || (c1[3] == null || c1[3] === 1) && ( // full opacity for colour 1?
17327 c2[3] == null || c2[3] === 1) // full opacity for colour 2?
17328 );
17329
17330 if (same) {
17331 return false;
17332 } // can't make a mapper without a range
17333
17334 }
17335
17336 return {
17337 name: name,
17338 value: mapData,
17339 strValue: '' + value,
17340 mapped: _mapped,
17341 field: mapData[1],
17342 fieldMin: parseFloat(mapData[2]),
17343 // min & max are numeric
17344 fieldMax: parseFloat(mapData[3]),
17345 valueMin: valueMin.value,
17346 valueMax: valueMax.value,
17347 bypass: propIsBypass
17348 };
17349 }
17350
17351 if (type.multiple && propIsFlat !== 'multiple') {
17352 var vals;
17353
17354 if (valueIsString) {
17355 vals = value.split(/\s+/);
17356 } else if (array(value)) {
17357 vals = value;
17358 } else {
17359 vals = [value];
17360 }
17361
17362 if (type.evenMultiple && vals.length % 2 !== 0) {
17363 return null;
17364 }
17365
17366 var valArr = [];
17367 var unitsArr = [];
17368 var pfValArr = [];
17369 var strVal = '';
17370 var hasEnum = false;
17371
17372 for (var i = 0; i < vals.length; i++) {
17373 var p = self.parse(name, vals[i], propIsBypass, 'multiple');
17374 hasEnum = hasEnum || string(p.value);
17375 valArr.push(p.value);
17376 pfValArr.push(p.pfValue != null ? p.pfValue : p.value);
17377 unitsArr.push(p.units);
17378 strVal += (i > 0 ? ' ' : '') + p.strValue;
17379 }
17380
17381 if (type.validate && !type.validate(valArr, unitsArr)) {
17382 return null;
17383 }
17384
17385 if (type.singleEnum && hasEnum) {
17386 if (valArr.length === 1 && string(valArr[0])) {
17387 return {
17388 name: name,
17389 value: valArr[0],
17390 strValue: valArr[0],
17391 bypass: propIsBypass
17392 };
17393 } else {
17394 return null;
17395 }
17396 }
17397
17398 return {
17399 name: name,
17400 value: valArr,
17401 pfValue: pfValArr,
17402 strValue: strVal,
17403 bypass: propIsBypass,
17404 units: unitsArr
17405 };
17406 } // several types also allow enums
17407
17408
17409 var checkEnums = function checkEnums() {
17410 for (var _i = 0; _i < type.enums.length; _i++) {
17411 var en = type.enums[_i];
17412
17413 if (en === value) {
17414 return {
17415 name: name,
17416 value: value,
17417 strValue: '' + value,
17418 bypass: propIsBypass
17419 };
17420 }
17421 }
17422
17423 return null;
17424 }; // check the type and return the appropriate object
17425
17426
17427 if (type.number) {
17428 var units;
17429 var implicitUnits = 'px'; // not set => px
17430
17431 if (type.units) {
17432 // use specified units if set
17433 units = type.units;
17434 }
17435
17436 if (type.implicitUnits) {
17437 implicitUnits = type.implicitUnits;
17438 }
17439
17440 if (!type.unitless) {
17441 if (valueIsString) {
17442 var unitsRegex = 'px|em' + (type.allowPercent ? '|\\%' : '');
17443
17444 if (units) {
17445 unitsRegex = units;
17446 } // only allow explicit units if so set
17447
17448
17449 var match = value.match('^(' + number$1 + ')(' + unitsRegex + ')?' + '$');
17450
17451 if (match) {
17452 value = match[1];
17453 units = match[2] || implicitUnits;
17454 }
17455 } else if (!units || type.implicitUnits) {
17456 units = implicitUnits; // implicitly px if unspecified
17457 }
17458 }
17459
17460 value = parseFloat(value); // if not a number and enums not allowed, then the value is invalid
17461
17462 if (isNaN(value) && type.enums === undefined) {
17463 return null;
17464 } // check if this number type also accepts special keywords in place of numbers
17465 // (i.e. `left`, `auto`, etc)
17466
17467
17468 if (isNaN(value) && type.enums !== undefined) {
17469 value = passedValue;
17470 return checkEnums();
17471 } // check if value must be an integer
17472
17473
17474 if (type.integer && !integer(value)) {
17475 return null;
17476 } // check value is within range
17477
17478
17479 if (type.min !== undefined && (value < type.min || type.strictMin && value === type.min) || type.max !== undefined && (value > type.max || type.strictMax && value === type.max)) {
17480 return null;
17481 }
17482
17483 var ret = {
17484 name: name,
17485 value: value,
17486 strValue: '' + value + (units ? units : ''),
17487 units: units,
17488 bypass: propIsBypass
17489 }; // normalise value in pixels
17490
17491 if (type.unitless || units !== 'px' && units !== 'em') {
17492 ret.pfValue = value;
17493 } else {
17494 ret.pfValue = units === 'px' || !units ? value : this.getEmSizeInPixels() * value;
17495 } // normalise value in ms
17496
17497
17498 if (units === 'ms' || units === 's') {
17499 ret.pfValue = units === 'ms' ? value : 1000 * value;
17500 } // normalise value in rad
17501
17502
17503 if (units === 'deg' || units === 'rad') {
17504 ret.pfValue = units === 'rad' ? value : deg2rad(value);
17505 } // normalize value in %
17506
17507
17508 if (units === '%') {
17509 ret.pfValue = value / 100;
17510 }
17511
17512 return ret;
17513 } else if (type.propList) {
17514 var props = [];
17515 var propsStr = '' + value;
17516
17517 if (propsStr === 'none') ; else {
17518 // go over each prop
17519 var propsSplit = propsStr.split(/\s*,\s*|\s+/);
17520
17521 for (var _i2 = 0; _i2 < propsSplit.length; _i2++) {
17522 var propName = propsSplit[_i2].trim();
17523
17524 if (self.properties[propName]) {
17525 props.push(propName);
17526 } else {
17527 warn('`' + propName + '` is not a valid property name');
17528 }
17529 }
17530
17531 if (props.length === 0) {
17532 return null;
17533 }
17534 }
17535
17536 return {
17537 name: name,
17538 value: props,
17539 strValue: props.length === 0 ? 'none' : props.join(' '),
17540 bypass: propIsBypass
17541 };
17542 } else if (type.color) {
17543 var tuple = color2tuple(value);
17544
17545 if (!tuple) {
17546 return null;
17547 }
17548
17549 return {
17550 name: name,
17551 value: tuple,
17552 pfValue: tuple,
17553 strValue: 'rgb(' + tuple[0] + ',' + tuple[1] + ',' + tuple[2] + ')',
17554 // n.b. no spaces b/c of multiple support
17555 bypass: propIsBypass
17556 };
17557 } else if (type.regex || type.regexes) {
17558 // first check enums
17559 if (type.enums) {
17560 var enumProp = checkEnums();
17561
17562 if (enumProp) {
17563 return enumProp;
17564 }
17565 }
17566
17567 var regexes = type.regexes ? type.regexes : [type.regex];
17568
17569 for (var _i3 = 0; _i3 < regexes.length; _i3++) {
17570 var regex = new RegExp(regexes[_i3]); // make a regex from the type string
17571
17572 var m = regex.exec(value);
17573
17574 if (m) {
17575 // regex matches
17576 return {
17577 name: name,
17578 value: type.singleRegexMatchValue ? m[1] : m,
17579 strValue: '' + value,
17580 bypass: propIsBypass
17581 };
17582 }
17583 }
17584
17585 return null; // didn't match any
17586 } else if (type.string) {
17587 // just return
17588 return {
17589 name: name,
17590 value: '' + value,
17591 strValue: '' + value,
17592 bypass: propIsBypass
17593 };
17594 } else if (type.enums) {
17595 // check enums last because it's a combo type in others
17596 return checkEnums();
17597 } else {
17598 return null; // not a type we can handle
17599 }
17600};
17601
17602var Style = function Style(cy) {
17603 if (!(this instanceof Style)) {
17604 return new Style(cy);
17605 }
17606
17607 if (!core(cy)) {
17608 error('A style must have a core reference');
17609 return;
17610 }
17611
17612 this._private = {
17613 cy: cy,
17614 coreStyle: {}
17615 };
17616 this.length = 0;
17617 this.resetToDefault();
17618};
17619
17620var styfn$8 = Style.prototype;
17621
17622styfn$8.instanceString = function () {
17623 return 'style';
17624}; // remove all contexts
17625
17626
17627styfn$8.clear = function () {
17628 var _p = this._private;
17629 var cy = _p.cy;
17630 var eles = cy.elements();
17631
17632 for (var i = 0; i < this.length; i++) {
17633 this[i] = undefined;
17634 }
17635
17636 this.length = 0;
17637 _p.contextStyles = {};
17638 _p.propDiffs = {};
17639 this.cleanElements(eles, true);
17640 eles.forEach(function (ele) {
17641 var ele_p = ele[0]._private;
17642 ele_p.styleDirty = true;
17643 ele_p.appliedInitStyle = false;
17644 });
17645 return this; // chaining
17646};
17647
17648styfn$8.resetToDefault = function () {
17649 this.clear();
17650 this.addDefaultStylesheet();
17651 return this;
17652}; // builds a style object for the 'core' selector
17653
17654
17655styfn$8.core = function (propName) {
17656 return this._private.coreStyle[propName] || this.getDefaultProperty(propName);
17657}; // create a new context from the specified selector string and switch to that context
17658
17659
17660styfn$8.selector = function (selectorStr) {
17661 // 'core' is a special case and does not need a selector
17662 var selector = selectorStr === 'core' ? null : new Selector(selectorStr);
17663 var i = this.length++; // new context means new index
17664
17665 this[i] = {
17666 selector: selector,
17667 properties: [],
17668 mappedProperties: [],
17669 index: i
17670 };
17671 return this; // chaining
17672}; // add one or many css rules to the current context
17673
17674
17675styfn$8.css = function () {
17676 var self = this;
17677 var args = arguments;
17678
17679 if (args.length === 1) {
17680 var map = args[0];
17681
17682 for (var i = 0; i < self.properties.length; i++) {
17683 var prop = self.properties[i];
17684 var mapVal = map[prop.name];
17685
17686 if (mapVal === undefined) {
17687 mapVal = map[dash2camel(prop.name)];
17688 }
17689
17690 if (mapVal !== undefined) {
17691 this.cssRule(prop.name, mapVal);
17692 }
17693 }
17694 } else if (args.length === 2) {
17695 this.cssRule(args[0], args[1]);
17696 } // do nothing if args are invalid
17697
17698
17699 return this; // chaining
17700};
17701
17702styfn$8.style = styfn$8.css; // add a single css rule to the current context
17703
17704styfn$8.cssRule = function (name, value) {
17705 // name-value pair
17706 var property = this.parse(name, value); // add property to current context if valid
17707
17708 if (property) {
17709 var i = this.length - 1;
17710 this[i].properties.push(property);
17711 this[i].properties[property.name] = property; // allow access by name as well
17712
17713 if (property.name.match(/pie-(\d+)-background-size/) && property.value) {
17714 this._private.hasPie = true;
17715 }
17716
17717 if (property.mapped) {
17718 this[i].mappedProperties.push(property);
17719 } // add to core style if necessary
17720
17721
17722 var currentSelectorIsCore = !this[i].selector;
17723
17724 if (currentSelectorIsCore) {
17725 this._private.coreStyle[property.name] = property;
17726 }
17727 }
17728
17729 return this; // chaining
17730};
17731
17732styfn$8.append = function (style) {
17733 if (stylesheet(style)) {
17734 style.appendToStyle(this);
17735 } else if (array(style)) {
17736 this.appendFromJson(style);
17737 } else if (string(style)) {
17738 this.appendFromString(style);
17739 } // you probably wouldn't want to append a Style, since you'd duplicate the default parts
17740
17741
17742 return this;
17743}; // static function
17744
17745
17746Style.fromJson = function (cy, json) {
17747 var style = new Style(cy);
17748 style.fromJson(json);
17749 return style;
17750};
17751
17752Style.fromString = function (cy, string) {
17753 return new Style(cy).fromString(string);
17754};
17755
17756[styfn, styfn$1, styfn$2, styfn$3, styfn$4, styfn$5, styfn$6, styfn$7].forEach(function (props) {
17757 extend(styfn$8, props);
17758});
17759Style.types = styfn$8.types;
17760Style.properties = styfn$8.properties;
17761Style.propertyGroups = styfn$8.propertyGroups;
17762Style.propertyGroupNames = styfn$8.propertyGroupNames;
17763Style.propertyGroupKeys = styfn$8.propertyGroupKeys;
17764
17765var corefn$7 = {
17766 style: function style(newStyle) {
17767 if (newStyle) {
17768 var s = this.setStyle(newStyle);
17769 s.update();
17770 }
17771
17772 return this._private.style;
17773 },
17774 setStyle: function setStyle(style) {
17775 var _p = this._private;
17776
17777 if (stylesheet(style)) {
17778 _p.style = style.generateStyle(this);
17779 } else if (array(style)) {
17780 _p.style = Style.fromJson(this, style);
17781 } else if (string(style)) {
17782 _p.style = Style.fromString(this, style);
17783 } else {
17784 _p.style = Style(this);
17785 }
17786
17787 return _p.style;
17788 },
17789 // e.g. cy.data() changed => recalc ele mappers
17790 updateStyle: function updateStyle() {
17791 this.mutableElements().updateStyle(); // just send to all eles
17792 }
17793};
17794
17795var defaultSelectionType = 'single';
17796var corefn$8 = {
17797 autolock: function autolock(bool) {
17798 if (bool !== undefined) {
17799 this._private.autolock = bool ? true : false;
17800 } else {
17801 return this._private.autolock;
17802 }
17803
17804 return this; // chaining
17805 },
17806 autoungrabify: function autoungrabify(bool) {
17807 if (bool !== undefined) {
17808 this._private.autoungrabify = bool ? true : false;
17809 } else {
17810 return this._private.autoungrabify;
17811 }
17812
17813 return this; // chaining
17814 },
17815 autounselectify: function autounselectify(bool) {
17816 if (bool !== undefined) {
17817 this._private.autounselectify = bool ? true : false;
17818 } else {
17819 return this._private.autounselectify;
17820 }
17821
17822 return this; // chaining
17823 },
17824 selectionType: function selectionType(selType) {
17825 var _p = this._private;
17826
17827 if (_p.selectionType == null) {
17828 _p.selectionType = defaultSelectionType;
17829 }
17830
17831 if (selType !== undefined) {
17832 if (selType === 'additive' || selType === 'single') {
17833 _p.selectionType = selType;
17834 }
17835 } else {
17836 return _p.selectionType;
17837 }
17838
17839 return this;
17840 },
17841 panningEnabled: function panningEnabled(bool) {
17842 if (bool !== undefined) {
17843 this._private.panningEnabled = bool ? true : false;
17844 } else {
17845 return this._private.panningEnabled;
17846 }
17847
17848 return this; // chaining
17849 },
17850 userPanningEnabled: function userPanningEnabled(bool) {
17851 if (bool !== undefined) {
17852 this._private.userPanningEnabled = bool ? true : false;
17853 } else {
17854 return this._private.userPanningEnabled;
17855 }
17856
17857 return this; // chaining
17858 },
17859 zoomingEnabled: function zoomingEnabled(bool) {
17860 if (bool !== undefined) {
17861 this._private.zoomingEnabled = bool ? true : false;
17862 } else {
17863 return this._private.zoomingEnabled;
17864 }
17865
17866 return this; // chaining
17867 },
17868 userZoomingEnabled: function userZoomingEnabled(bool) {
17869 if (bool !== undefined) {
17870 this._private.userZoomingEnabled = bool ? true : false;
17871 } else {
17872 return this._private.userZoomingEnabled;
17873 }
17874
17875 return this; // chaining
17876 },
17877 boxSelectionEnabled: function boxSelectionEnabled(bool) {
17878 if (bool !== undefined) {
17879 this._private.boxSelectionEnabled = bool ? true : false;
17880 } else {
17881 return this._private.boxSelectionEnabled;
17882 }
17883
17884 return this; // chaining
17885 },
17886 pan: function pan() {
17887 var args = arguments;
17888 var pan = this._private.pan;
17889 var dim, val, dims, x, y;
17890
17891 switch (args.length) {
17892 case 0:
17893 // .pan()
17894 return pan;
17895
17896 case 1:
17897 if (string(args[0])) {
17898 // .pan('x')
17899 dim = args[0];
17900 return pan[dim];
17901 } else if (plainObject(args[0])) {
17902 // .pan({ x: 0, y: 100 })
17903 if (!this._private.panningEnabled) {
17904 return this;
17905 }
17906
17907 dims = args[0];
17908 x = dims.x;
17909 y = dims.y;
17910
17911 if (number(x)) {
17912 pan.x = x;
17913 }
17914
17915 if (number(y)) {
17916 pan.y = y;
17917 }
17918
17919 this.emit('pan viewport');
17920 }
17921
17922 break;
17923
17924 case 2:
17925 // .pan('x', 100)
17926 if (!this._private.panningEnabled) {
17927 return this;
17928 }
17929
17930 dim = args[0];
17931 val = args[1];
17932
17933 if ((dim === 'x' || dim === 'y') && number(val)) {
17934 pan[dim] = val;
17935 }
17936
17937 this.emit('pan viewport');
17938 break;
17939 // invalid
17940 }
17941
17942 this.notify('viewport');
17943 return this; // chaining
17944 },
17945 panBy: function panBy(arg0, arg1) {
17946 var args = arguments;
17947 var pan = this._private.pan;
17948 var dim, val, dims, x, y;
17949
17950 if (!this._private.panningEnabled) {
17951 return this;
17952 }
17953
17954 switch (args.length) {
17955 case 1:
17956 if (plainObject(arg0)) {
17957 // .panBy({ x: 0, y: 100 })
17958 dims = args[0];
17959 x = dims.x;
17960 y = dims.y;
17961
17962 if (number(x)) {
17963 pan.x += x;
17964 }
17965
17966 if (number(y)) {
17967 pan.y += y;
17968 }
17969
17970 this.emit('pan viewport');
17971 }
17972
17973 break;
17974
17975 case 2:
17976 // .panBy('x', 100)
17977 dim = arg0;
17978 val = arg1;
17979
17980 if ((dim === 'x' || dim === 'y') && number(val)) {
17981 pan[dim] += val;
17982 }
17983
17984 this.emit('pan viewport');
17985 break;
17986 // invalid
17987 }
17988
17989 this.notify('viewport');
17990 return this; // chaining
17991 },
17992 fit: function fit(elements, padding) {
17993 var viewportState = this.getFitViewport(elements, padding);
17994
17995 if (viewportState) {
17996 var _p = this._private;
17997 _p.zoom = viewportState.zoom;
17998 _p.pan = viewportState.pan;
17999 this.emit('pan zoom viewport');
18000 this.notify('viewport');
18001 }
18002
18003 return this; // chaining
18004 },
18005 getFitViewport: function getFitViewport(elements, padding) {
18006 if (number(elements) && padding === undefined) {
18007 // elements is optional
18008 padding = elements;
18009 elements = undefined;
18010 }
18011
18012 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18013 return;
18014 }
18015
18016 var bb;
18017
18018 if (string(elements)) {
18019 var sel = elements;
18020 elements = this.$(sel);
18021 } else if (boundingBox(elements)) {
18022 // assume bb
18023 var bbe = elements;
18024 bb = {
18025 x1: bbe.x1,
18026 y1: bbe.y1,
18027 x2: bbe.x2,
18028 y2: bbe.y2
18029 };
18030 bb.w = bb.x2 - bb.x1;
18031 bb.h = bb.y2 - bb.y1;
18032 } else if (!elementOrCollection(elements)) {
18033 elements = this.mutableElements();
18034 }
18035
18036 if (elementOrCollection(elements) && elements.empty()) {
18037 return;
18038 } // can't fit to nothing
18039
18040
18041 bb = bb || elements.boundingBox();
18042 var w = this.width();
18043 var h = this.height();
18044 var zoom;
18045 padding = number(padding) ? padding : 0;
18046
18047 if (!isNaN(w) && !isNaN(h) && w > 0 && h > 0 && !isNaN(bb.w) && !isNaN(bb.h) && bb.w > 0 && bb.h > 0) {
18048 zoom = Math.min((w - 2 * padding) / bb.w, (h - 2 * padding) / bb.h); // crop zoom
18049
18050 zoom = zoom > this._private.maxZoom ? this._private.maxZoom : zoom;
18051 zoom = zoom < this._private.minZoom ? this._private.minZoom : zoom;
18052 var pan = {
18053 // now pan to middle
18054 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18055 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18056 };
18057 return {
18058 zoom: zoom,
18059 pan: pan
18060 };
18061 }
18062
18063 return;
18064 },
18065 zoomRange: function zoomRange(min, max) {
18066 var _p = this._private;
18067
18068 if (max == null) {
18069 var opts = min;
18070 min = opts.min;
18071 max = opts.max;
18072 }
18073
18074 if (number(min) && number(max) && min <= max) {
18075 _p.minZoom = min;
18076 _p.maxZoom = max;
18077 } else if (number(min) && max === undefined && min <= _p.maxZoom) {
18078 _p.minZoom = min;
18079 } else if (number(max) && min === undefined && max >= _p.minZoom) {
18080 _p.maxZoom = max;
18081 }
18082
18083 return this;
18084 },
18085 minZoom: function minZoom(zoom) {
18086 if (zoom === undefined) {
18087 return this._private.minZoom;
18088 } else {
18089 return this.zoomRange({
18090 min: zoom
18091 });
18092 }
18093 },
18094 maxZoom: function maxZoom(zoom) {
18095 if (zoom === undefined) {
18096 return this._private.maxZoom;
18097 } else {
18098 return this.zoomRange({
18099 max: zoom
18100 });
18101 }
18102 },
18103 getZoomedViewport: function getZoomedViewport(params) {
18104 var _p = this._private;
18105 var currentPan = _p.pan;
18106 var currentZoom = _p.zoom;
18107 var pos; // in rendered px
18108
18109 var zoom;
18110 var bail = false;
18111
18112 if (!_p.zoomingEnabled) {
18113 // zooming disabled
18114 bail = true;
18115 }
18116
18117 if (number(params)) {
18118 // then set the zoom
18119 zoom = params;
18120 } else if (plainObject(params)) {
18121 // then zoom about a point
18122 zoom = params.level;
18123
18124 if (params.position != null) {
18125 pos = modelToRenderedPosition(params.position, currentZoom, currentPan);
18126 } else if (params.renderedPosition != null) {
18127 pos = params.renderedPosition;
18128 }
18129
18130 if (pos != null && !_p.panningEnabled) {
18131 // panning disabled
18132 bail = true;
18133 }
18134 } // crop zoom
18135
18136
18137 zoom = zoom > _p.maxZoom ? _p.maxZoom : zoom;
18138 zoom = zoom < _p.minZoom ? _p.minZoom : zoom; // can't zoom with invalid params
18139
18140 if (bail || !number(zoom) || zoom === currentZoom || pos != null && (!number(pos.x) || !number(pos.y))) {
18141 return null;
18142 }
18143
18144 if (pos != null) {
18145 // set zoom about position
18146 var pan1 = currentPan;
18147 var zoom1 = currentZoom;
18148 var zoom2 = zoom;
18149 var pan2 = {
18150 x: -zoom2 / zoom1 * (pos.x - pan1.x) + pos.x,
18151 y: -zoom2 / zoom1 * (pos.y - pan1.y) + pos.y
18152 };
18153 return {
18154 zoomed: true,
18155 panned: true,
18156 zoom: zoom2,
18157 pan: pan2
18158 };
18159 } else {
18160 // just set the zoom
18161 return {
18162 zoomed: true,
18163 panned: false,
18164 zoom: zoom,
18165 pan: currentPan
18166 };
18167 }
18168 },
18169 zoom: function zoom(params) {
18170 if (params === undefined) {
18171 // get
18172 return this._private.zoom;
18173 } else {
18174 // set
18175 var vp = this.getZoomedViewport(params);
18176 var _p = this._private;
18177
18178 if (vp == null || !vp.zoomed) {
18179 return this;
18180 }
18181
18182 _p.zoom = vp.zoom;
18183
18184 if (vp.panned) {
18185 _p.pan.x = vp.pan.x;
18186 _p.pan.y = vp.pan.y;
18187 }
18188
18189 this.emit('zoom' + (vp.panned ? ' pan' : '') + ' viewport');
18190 this.notify('viewport');
18191 return this; // chaining
18192 }
18193 },
18194 viewport: function viewport(opts) {
18195 var _p = this._private;
18196 var zoomDefd = true;
18197 var panDefd = true;
18198 var events = []; // to trigger
18199
18200 var zoomFailed = false;
18201 var panFailed = false;
18202
18203 if (!opts) {
18204 return this;
18205 }
18206
18207 if (!number(opts.zoom)) {
18208 zoomDefd = false;
18209 }
18210
18211 if (!plainObject(opts.pan)) {
18212 panDefd = false;
18213 }
18214
18215 if (!zoomDefd && !panDefd) {
18216 return this;
18217 }
18218
18219 if (zoomDefd) {
18220 var z = opts.zoom;
18221
18222 if (z < _p.minZoom || z > _p.maxZoom || !_p.zoomingEnabled) {
18223 zoomFailed = true;
18224 } else {
18225 _p.zoom = z;
18226 events.push('zoom');
18227 }
18228 }
18229
18230 if (panDefd && (!zoomFailed || !opts.cancelOnFailedZoom) && _p.panningEnabled) {
18231 var p = opts.pan;
18232
18233 if (number(p.x)) {
18234 _p.pan.x = p.x;
18235 panFailed = false;
18236 }
18237
18238 if (number(p.y)) {
18239 _p.pan.y = p.y;
18240 panFailed = false;
18241 }
18242
18243 if (!panFailed) {
18244 events.push('pan');
18245 }
18246 }
18247
18248 if (events.length > 0) {
18249 events.push('viewport');
18250 this.emit(events.join(' '));
18251 this.notify('viewport');
18252 }
18253
18254 return this; // chaining
18255 },
18256 center: function center(elements) {
18257 var pan = this.getCenterPan(elements);
18258
18259 if (pan) {
18260 this._private.pan = pan;
18261 this.emit('pan viewport');
18262 this.notify('viewport');
18263 }
18264
18265 return this; // chaining
18266 },
18267 getCenterPan: function getCenterPan(elements, zoom) {
18268 if (!this._private.panningEnabled) {
18269 return;
18270 }
18271
18272 if (string(elements)) {
18273 var selector = elements;
18274 elements = this.mutableElements().filter(selector);
18275 } else if (!elementOrCollection(elements)) {
18276 elements = this.mutableElements();
18277 }
18278
18279 if (elements.length === 0) {
18280 return;
18281 } // can't centre pan to nothing
18282
18283
18284 var bb = elements.boundingBox();
18285 var w = this.width();
18286 var h = this.height();
18287 zoom = zoom === undefined ? this._private.zoom : zoom;
18288 var pan = {
18289 // middle
18290 x: (w - zoom * (bb.x1 + bb.x2)) / 2,
18291 y: (h - zoom * (bb.y1 + bb.y2)) / 2
18292 };
18293 return pan;
18294 },
18295 reset: function reset() {
18296 if (!this._private.panningEnabled || !this._private.zoomingEnabled) {
18297 return this;
18298 }
18299
18300 this.viewport({
18301 pan: {
18302 x: 0,
18303 y: 0
18304 },
18305 zoom: 1
18306 });
18307 return this; // chaining
18308 },
18309 invalidateSize: function invalidateSize() {
18310 this._private.sizeCache = null;
18311 },
18312 size: function size() {
18313 var _p = this._private;
18314 var container = _p.container;
18315 return _p.sizeCache = _p.sizeCache || (container ? function () {
18316 var style = window$1.getComputedStyle(container);
18317
18318 var val = function val(name) {
18319 return parseFloat(style.getPropertyValue(name));
18320 };
18321
18322 return {
18323 width: container.clientWidth - val('padding-left') - val('padding-right'),
18324 height: container.clientHeight - val('padding-top') - val('padding-bottom')
18325 };
18326 }() : {
18327 // fallback if no container (not 0 b/c can be used for dividing etc)
18328 width: 1,
18329 height: 1
18330 });
18331 },
18332 width: function width() {
18333 return this.size().width;
18334 },
18335 height: function height() {
18336 return this.size().height;
18337 },
18338 extent: function extent() {
18339 var pan = this._private.pan;
18340 var zoom = this._private.zoom;
18341 var rb = this.renderedExtent();
18342 var b = {
18343 x1: (rb.x1 - pan.x) / zoom,
18344 x2: (rb.x2 - pan.x) / zoom,
18345 y1: (rb.y1 - pan.y) / zoom,
18346 y2: (rb.y2 - pan.y) / zoom
18347 };
18348 b.w = b.x2 - b.x1;
18349 b.h = b.y2 - b.y1;
18350 return b;
18351 },
18352 renderedExtent: function renderedExtent() {
18353 var width = this.width();
18354 var height = this.height();
18355 return {
18356 x1: 0,
18357 y1: 0,
18358 x2: width,
18359 y2: height,
18360 w: width,
18361 h: height
18362 };
18363 }
18364}; // aliases
18365
18366corefn$8.centre = corefn$8.center; // backwards compatibility
18367
18368corefn$8.autolockNodes = corefn$8.autolock;
18369corefn$8.autoungrabifyNodes = corefn$8.autoungrabify;
18370
18371var fn$6 = {
18372 data: define$3.data({
18373 field: 'data',
18374 bindingEvent: 'data',
18375 allowBinding: true,
18376 allowSetting: true,
18377 settingEvent: 'data',
18378 settingTriggersEvent: true,
18379 triggerFnName: 'trigger',
18380 allowGetting: true,
18381 updateStyle: true
18382 }),
18383 removeData: define$3.removeData({
18384 field: 'data',
18385 event: 'data',
18386 triggerFnName: 'trigger',
18387 triggerEvent: true,
18388 updateStyle: true
18389 }),
18390 scratch: define$3.data({
18391 field: 'scratch',
18392 bindingEvent: 'scratch',
18393 allowBinding: true,
18394 allowSetting: true,
18395 settingEvent: 'scratch',
18396 settingTriggersEvent: true,
18397 triggerFnName: 'trigger',
18398 allowGetting: true,
18399 updateStyle: true
18400 }),
18401 removeScratch: define$3.removeData({
18402 field: 'scratch',
18403 event: 'scratch',
18404 triggerFnName: 'trigger',
18405 triggerEvent: true,
18406 updateStyle: true
18407 })
18408}; // aliases
18409
18410fn$6.attr = fn$6.data;
18411fn$6.removeAttr = fn$6.removeData;
18412
18413var Core = function Core(opts) {
18414 var cy = this;
18415 opts = extend({}, opts);
18416 var container = opts.container; // allow for passing a wrapped jquery object
18417 // e.g. cytoscape({ container: $('#cy') })
18418
18419 if (container && !htmlElement(container) && htmlElement(container[0])) {
18420 container = container[0];
18421 }
18422
18423 var reg = container ? container._cyreg : null; // e.g. already registered some info (e.g. readies) via jquery
18424
18425 reg = reg || {};
18426
18427 if (reg && reg.cy) {
18428 reg.cy.destroy();
18429 reg = {}; // old instance => replace reg completely
18430 }
18431
18432 var readies = reg.readies = reg.readies || [];
18433
18434 if (container) {
18435 container._cyreg = reg;
18436 } // make sure container assoc'd reg points to this cy
18437
18438
18439 reg.cy = cy;
18440 var head = window$1 !== undefined && container !== undefined && !opts.headless;
18441 var options = opts;
18442 options.layout = extend({
18443 name: head ? 'grid' : 'null'
18444 }, options.layout);
18445 options.renderer = extend({
18446 name: head ? 'canvas' : 'null'
18447 }, options.renderer);
18448
18449 var defVal = function defVal(def, val, altVal) {
18450 if (val !== undefined) {
18451 return val;
18452 } else if (altVal !== undefined) {
18453 return altVal;
18454 } else {
18455 return def;
18456 }
18457 };
18458
18459 var _p = this._private = {
18460 container: container,
18461 // html dom ele container
18462 ready: false,
18463 // whether ready has been triggered
18464 options: options,
18465 // cached options
18466 elements: new Collection(this),
18467 // elements in the graph
18468 listeners: [],
18469 // list of listeners
18470 aniEles: new Collection(this),
18471 // elements being animated
18472 data: options.data || {},
18473 // data for the core
18474 scratch: {},
18475 // scratch object for core
18476 layout: null,
18477 renderer: null,
18478 destroyed: false,
18479 // whether destroy was called
18480 notificationsEnabled: true,
18481 // whether notifications are sent to the renderer
18482 minZoom: 1e-50,
18483 maxZoom: 1e50,
18484 zoomingEnabled: defVal(true, options.zoomingEnabled),
18485 userZoomingEnabled: defVal(true, options.userZoomingEnabled),
18486 panningEnabled: defVal(true, options.panningEnabled),
18487 userPanningEnabled: defVal(true, options.userPanningEnabled),
18488 boxSelectionEnabled: defVal(true, options.boxSelectionEnabled),
18489 autolock: defVal(false, options.autolock, options.autolockNodes),
18490 autoungrabify: defVal(false, options.autoungrabify, options.autoungrabifyNodes),
18491 autounselectify: defVal(false, options.autounselectify),
18492 styleEnabled: options.styleEnabled === undefined ? head : options.styleEnabled,
18493 zoom: number(options.zoom) ? options.zoom : 1,
18494 pan: {
18495 x: plainObject(options.pan) && number(options.pan.x) ? options.pan.x : 0,
18496 y: plainObject(options.pan) && number(options.pan.y) ? options.pan.y : 0
18497 },
18498 animation: {
18499 // object for currently-running animations
18500 current: [],
18501 queue: []
18502 },
18503 hasCompoundNodes: false
18504 };
18505
18506 this.createEmitter(); // set selection type
18507
18508 this.selectionType(options.selectionType); // init zoom bounds
18509
18510 this.zoomRange({
18511 min: options.minZoom,
18512 max: options.maxZoom
18513 });
18514
18515 var loadExtData = function loadExtData(extData, next) {
18516 var anyIsPromise = extData.some(promise);
18517
18518 if (anyIsPromise) {
18519 return Promise$1.all(extData).then(next); // load all data asynchronously, then exec rest of init
18520 } else {
18521 next(extData); // exec synchronously for convenience
18522 }
18523 }; // start with the default stylesheet so we have something before loading an external stylesheet
18524
18525
18526 if (_p.styleEnabled) {
18527 cy.setStyle([]);
18528 } // create the renderer
18529
18530
18531 var rendererOptions = extend({}, options, options.renderer); // allow rendering hints in top level options
18532
18533 cy.initRenderer(rendererOptions);
18534
18535 var setElesAndLayout = function setElesAndLayout(elements, onload, ondone) {
18536 cy.notifications(false); // remove old elements
18537
18538 var oldEles = cy.mutableElements();
18539
18540 if (oldEles.length > 0) {
18541 oldEles.remove();
18542 }
18543
18544 if (elements != null) {
18545 if (plainObject(elements) || array(elements)) {
18546 cy.add(elements);
18547 }
18548 }
18549
18550 cy.one('layoutready', function (e) {
18551 cy.notifications(true);
18552 cy.emit(e); // we missed this event by turning notifications off, so pass it on
18553
18554 cy.one('load', onload);
18555 cy.emitAndNotify('load');
18556 }).one('layoutstop', function () {
18557 cy.one('done', ondone);
18558 cy.emit('done');
18559 });
18560 var layoutOpts = extend({}, cy._private.options.layout);
18561 layoutOpts.eles = cy.elements();
18562 cy.layout(layoutOpts).run();
18563 };
18564
18565 loadExtData([options.style, options.elements], function (thens) {
18566 var initStyle = thens[0];
18567 var initEles = thens[1]; // init style
18568
18569 if (_p.styleEnabled) {
18570 cy.style().append(initStyle);
18571 } // initial load
18572
18573
18574 setElesAndLayout(initEles, function () {
18575 // onready
18576 cy.startAnimationLoop();
18577 _p.ready = true; // if a ready callback is specified as an option, the bind it
18578
18579 if (fn(options.ready)) {
18580 cy.on('ready', options.ready);
18581 } // bind all the ready handlers registered before creating this instance
18582
18583
18584 for (var i = 0; i < readies.length; i++) {
18585 var fn$1 = readies[i];
18586 cy.on('ready', fn$1);
18587 }
18588
18589 if (reg) {
18590 reg.readies = [];
18591 } // 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
18592
18593
18594 cy.emit('ready');
18595 }, options.done);
18596 });
18597};
18598
18599var corefn$9 = Core.prototype; // short alias
18600
18601extend(corefn$9, {
18602 instanceString: function instanceString() {
18603 return 'core';
18604 },
18605 isReady: function isReady() {
18606 return this._private.ready;
18607 },
18608 destroyed: function destroyed() {
18609 return this._private.destroyed;
18610 },
18611 ready: function ready(fn) {
18612 if (this.isReady()) {
18613 this.emitter().emit('ready', [], fn); // just calls fn as though triggered via ready event
18614 } else {
18615 this.on('ready', fn);
18616 }
18617
18618 return this;
18619 },
18620 destroy: function destroy() {
18621 var cy = this;
18622 if (cy.destroyed()) return;
18623 cy.stopAnimationLoop();
18624 cy.destroyRenderer();
18625 this.emit('destroy');
18626 cy._private.destroyed = true;
18627 return cy;
18628 },
18629 hasElementWithId: function hasElementWithId(id) {
18630 return this._private.elements.hasElementWithId(id);
18631 },
18632 getElementById: function getElementById(id) {
18633 return this._private.elements.getElementById(id);
18634 },
18635 hasCompoundNodes: function hasCompoundNodes() {
18636 return this._private.hasCompoundNodes;
18637 },
18638 headless: function headless() {
18639 return this._private.renderer.isHeadless();
18640 },
18641 styleEnabled: function styleEnabled() {
18642 return this._private.styleEnabled;
18643 },
18644 addToPool: function addToPool(eles) {
18645 this._private.elements.merge(eles);
18646
18647 return this; // chaining
18648 },
18649 removeFromPool: function removeFromPool(eles) {
18650 this._private.elements.unmerge(eles);
18651
18652 return this;
18653 },
18654 container: function container() {
18655 return this._private.container || null;
18656 },
18657 mount: function mount(container) {
18658 if (container == null) {
18659 return;
18660 }
18661
18662 var cy = this;
18663 var _p = cy._private;
18664 var options = _p.options;
18665
18666 if (!htmlElement(container) && htmlElement(container[0])) {
18667 container = container[0];
18668 }
18669
18670 cy.stopAnimationLoop();
18671 cy.destroyRenderer();
18672 _p.container = container;
18673 _p.styleEnabled = true;
18674 cy.invalidateSize();
18675 cy.initRenderer(extend({}, options, options.renderer, {
18676 // allow custom renderer name to be re-used, otherwise use canvas
18677 name: options.renderer.name === 'null' ? 'canvas' : options.renderer.name
18678 }));
18679 cy.startAnimationLoop();
18680 cy.style(options.style);
18681 cy.emit('mount');
18682 return cy;
18683 },
18684 unmount: function unmount() {
18685 var cy = this;
18686 cy.stopAnimationLoop();
18687 cy.destroyRenderer();
18688 cy.initRenderer({
18689 name: 'null'
18690 });
18691 cy.emit('unmount');
18692 return cy;
18693 },
18694 options: function options() {
18695 return copy(this._private.options);
18696 },
18697 json: function json(obj) {
18698 var cy = this;
18699 var _p = cy._private;
18700 var eles = cy.mutableElements();
18701
18702 var getFreshRef = function getFreshRef(ele) {
18703 return cy.getElementById(ele.id());
18704 };
18705
18706 if (plainObject(obj)) {
18707 // set
18708 cy.startBatch();
18709
18710 if (obj.elements) {
18711 var idInJson = {};
18712
18713 var updateEles = function updateEles(jsons, gr) {
18714 var toAdd = [];
18715 var toMod = [];
18716
18717 for (var i = 0; i < jsons.length; i++) {
18718 var json = jsons[i];
18719
18720 if (!json.data.id) {
18721 warn('cy.json() cannot handle elements without an ID attribute');
18722 continue;
18723 }
18724
18725 var id = '' + json.data.id; // id must be string
18726
18727 var ele = cy.getElementById(id);
18728 idInJson[id] = true;
18729
18730 if (ele.length !== 0) {
18731 // existing element should be updated
18732 toMod.push({
18733 ele: ele,
18734 json: json
18735 });
18736 } else {
18737 // otherwise should be added
18738 if (gr) {
18739 json.group = gr;
18740 toAdd.push(json);
18741 } else {
18742 toAdd.push(json);
18743 }
18744 }
18745 }
18746
18747 cy.add(toAdd);
18748
18749 for (var _i = 0; _i < toMod.length; _i++) {
18750 var _toMod$_i = toMod[_i],
18751 _ele = _toMod$_i.ele,
18752 _json = _toMod$_i.json;
18753
18754 _ele.json(_json);
18755 }
18756 };
18757
18758 if (array(obj.elements)) {
18759 // elements: []
18760 updateEles(obj.elements);
18761 } else {
18762 // elements: { nodes: [], edges: [] }
18763 var grs = ['nodes', 'edges'];
18764
18765 for (var i = 0; i < grs.length; i++) {
18766 var gr = grs[i];
18767 var elements = obj.elements[gr];
18768
18769 if (array(elements)) {
18770 updateEles(elements, gr);
18771 }
18772 }
18773 }
18774
18775 var parentsToRemove = cy.collection();
18776 eles.filter(function (ele) {
18777 return !idInJson[ele.id()];
18778 }).forEach(function (ele) {
18779 if (ele.isParent()) {
18780 parentsToRemove.merge(ele);
18781 } else {
18782 ele.remove();
18783 }
18784 }); // so that children are not removed w/parent
18785
18786 parentsToRemove.forEach(function (ele) {
18787 return ele.children().move({
18788 parent: null
18789 });
18790 }); // intermediate parents may be moved by prior line, so make sure we remove by fresh refs
18791
18792 parentsToRemove.forEach(function (ele) {
18793 return getFreshRef(ele).remove();
18794 });
18795 }
18796
18797 if (obj.style) {
18798 cy.style(obj.style);
18799 }
18800
18801 if (obj.zoom != null && obj.zoom !== _p.zoom) {
18802 cy.zoom(obj.zoom);
18803 }
18804
18805 if (obj.pan) {
18806 if (obj.pan.x !== _p.pan.x || obj.pan.y !== _p.pan.y) {
18807 cy.pan(obj.pan);
18808 }
18809 }
18810
18811 if (obj.data) {
18812 cy.data(obj.data);
18813 }
18814
18815 var fields = ['minZoom', 'maxZoom', 'zoomingEnabled', 'userZoomingEnabled', 'panningEnabled', 'userPanningEnabled', 'boxSelectionEnabled', 'autolock', 'autoungrabify', 'autounselectify'];
18816
18817 for (var _i2 = 0; _i2 < fields.length; _i2++) {
18818 var f = fields[_i2];
18819
18820 if (obj[f] != null) {
18821 cy[f](obj[f]);
18822 }
18823 }
18824
18825 cy.endBatch();
18826 return this; // chaining
18827 } else {
18828 // get
18829 var flat = !!obj;
18830 var json = {};
18831
18832 if (flat) {
18833 json.elements = this.elements().map(function (ele) {
18834 return ele.json();
18835 });
18836 } else {
18837 json.elements = {};
18838 eles.forEach(function (ele) {
18839 var group = ele.group();
18840
18841 if (!json.elements[group]) {
18842 json.elements[group] = [];
18843 }
18844
18845 json.elements[group].push(ele.json());
18846 });
18847 }
18848
18849 if (this._private.styleEnabled) {
18850 json.style = cy.style().json();
18851 }
18852
18853 json.data = copy(cy.data());
18854 var options = _p.options;
18855 json.zoomingEnabled = _p.zoomingEnabled;
18856 json.userZoomingEnabled = _p.userZoomingEnabled;
18857 json.zoom = _p.zoom;
18858 json.minZoom = _p.minZoom;
18859 json.maxZoom = _p.maxZoom;
18860 json.panningEnabled = _p.panningEnabled;
18861 json.userPanningEnabled = _p.userPanningEnabled;
18862 json.pan = copy(_p.pan);
18863 json.boxSelectionEnabled = _p.boxSelectionEnabled;
18864 json.renderer = copy(options.renderer);
18865 json.hideEdgesOnViewport = options.hideEdgesOnViewport;
18866 json.textureOnViewport = options.textureOnViewport;
18867 json.wheelSensitivity = options.wheelSensitivity;
18868 json.motionBlur = options.motionBlur;
18869 return json;
18870 }
18871 }
18872});
18873corefn$9.$id = corefn$9.getElementById;
18874[corefn, corefn$1, elesfn$v, corefn$2, corefn$3, corefn$4, corefn$5, corefn$6, corefn$7, corefn$8, fn$6].forEach(function (props) {
18875 extend(corefn$9, props);
18876});
18877
18878/* eslint-disable no-unused-vars */
18879
18880var defaults$9 = {
18881 fit: true,
18882 // whether to fit the viewport to the graph
18883 directed: false,
18884 // whether the tree is directed downwards (or edges can point in any direction if false)
18885 padding: 30,
18886 // padding on fit
18887 circle: false,
18888 // put depths in concentric circles if true, put depths top down if false
18889 grid: false,
18890 // whether to create an even grid into which the DAG is placed (circle:false only)
18891 spacingFactor: 1.75,
18892 // positive spacing factor, larger => more space between nodes (N.B. n/a if causes overlap)
18893 boundingBox: undefined,
18894 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
18895 avoidOverlap: true,
18896 // prevents node overlap, may overflow boundingBox if not enough space
18897 nodeDimensionsIncludeLabels: false,
18898 // Excludes the label when calculating node bounding boxes for the layout algorithm
18899 roots: undefined,
18900 // the roots of the trees
18901 maximal: false,
18902 // whether to shift nodes down their natural BFS depths in order to avoid upwards edges (DAGS only)
18903 animate: false,
18904 // whether to transition the node positions
18905 animationDuration: 500,
18906 // duration of animation in ms if enabled
18907 animationEasing: undefined,
18908 // easing of animation if enabled,
18909 animateFilter: function animateFilter(node, i) {
18910 return true;
18911 },
18912 // 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
18913 ready: undefined,
18914 // callback on layoutready
18915 stop: undefined,
18916 // callback on layoutstop
18917 transform: function transform(node, position) {
18918 return position;
18919 } // transform a given node position. Useful for changing flow direction in discrete layouts
18920
18921};
18922/* eslint-enable */
18923
18924var getInfo = function getInfo(ele) {
18925 return ele.scratch('breadthfirst');
18926};
18927
18928var setInfo = function setInfo(ele, obj) {
18929 return ele.scratch('breadthfirst', obj);
18930};
18931
18932function BreadthFirstLayout(options) {
18933 this.options = extend({}, defaults$9, options);
18934}
18935
18936BreadthFirstLayout.prototype.run = function () {
18937 var params = this.options;
18938 var options = params;
18939 var cy = params.cy;
18940 var eles = options.eles;
18941 var nodes = eles.nodes().filter(function (n) {
18942 return !n.isParent();
18943 });
18944 var graph = eles;
18945 var directed = options.directed;
18946 var maximal = options.maximal || options.maximalAdjustments > 0; // maximalAdjustments for compat. w/ old code
18947
18948 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
18949 x1: 0,
18950 y1: 0,
18951 w: cy.width(),
18952 h: cy.height()
18953 });
18954 var roots;
18955
18956 if (elementOrCollection(options.roots)) {
18957 roots = options.roots;
18958 } else if (array(options.roots)) {
18959 var rootsArray = [];
18960
18961 for (var i = 0; i < options.roots.length; i++) {
18962 var id = options.roots[i];
18963 var ele = cy.getElementById(id);
18964 rootsArray.push(ele);
18965 }
18966
18967 roots = cy.collection(rootsArray);
18968 } else if (string(options.roots)) {
18969 roots = cy.$(options.roots);
18970 } else {
18971 if (directed) {
18972 roots = nodes.roots();
18973 } else {
18974 var components = eles.components();
18975 roots = cy.collection();
18976
18977 var _loop = function _loop(_i) {
18978 var comp = components[_i];
18979 var maxDegree = comp.maxDegree(false);
18980 var compRoots = comp.filter(function (ele) {
18981 return ele.degree(false) === maxDegree;
18982 });
18983 roots = roots.add(compRoots);
18984 };
18985
18986 for (var _i = 0; _i < components.length; _i++) {
18987 _loop(_i);
18988 }
18989 }
18990 }
18991
18992 var depths = [];
18993 var foundByBfs = {};
18994
18995 var addToDepth = function addToDepth(ele, d) {
18996 if (depths[d] == null) {
18997 depths[d] = [];
18998 }
18999
19000 var i = depths[d].length;
19001 depths[d].push(ele);
19002 setInfo(ele, {
19003 index: i,
19004 depth: d
19005 });
19006 };
19007
19008 var changeDepth = function changeDepth(ele, newDepth) {
19009 var _getInfo = getInfo(ele),
19010 depth = _getInfo.depth,
19011 index = _getInfo.index;
19012
19013 depths[depth][index] = null;
19014 addToDepth(ele, newDepth);
19015 }; // find the depths of the nodes
19016
19017
19018 graph.bfs({
19019 roots: roots,
19020 directed: options.directed,
19021 visit: function visit(node, edge, pNode, i, depth) {
19022 var ele = node[0];
19023 var id = ele.id();
19024 addToDepth(ele, depth);
19025 foundByBfs[id] = true;
19026 }
19027 }); // check for nodes not found by bfs
19028
19029 var orphanNodes = [];
19030
19031 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
19032 var _ele = nodes[_i2];
19033
19034 if (foundByBfs[_ele.id()]) {
19035 continue;
19036 } else {
19037 orphanNodes.push(_ele);
19038 }
19039 } // assign the nodes a depth and index
19040
19041
19042 var assignDepthsAt = function assignDepthsAt(i) {
19043 var eles = depths[i];
19044
19045 for (var j = 0; j < eles.length; j++) {
19046 var _ele2 = eles[j];
19047
19048 if (_ele2 == null) {
19049 eles.splice(j, 1);
19050 j--;
19051 continue;
19052 }
19053
19054 setInfo(_ele2, {
19055 depth: i,
19056 index: j
19057 });
19058 }
19059 };
19060
19061 var assignDepths = function assignDepths() {
19062 for (var _i3 = 0; _i3 < depths.length; _i3++) {
19063 assignDepthsAt(_i3);
19064 }
19065 };
19066
19067 var adjustMaximally = function adjustMaximally(ele, shifted) {
19068 var eInfo = getInfo(ele);
19069 var incomers = ele.incomers().filter(function (el) {
19070 return el.isNode() && eles.has(el);
19071 });
19072 var maxDepth = -1;
19073 var id = ele.id();
19074
19075 for (var k = 0; k < incomers.length; k++) {
19076 var incmr = incomers[k];
19077 var iInfo = getInfo(incmr);
19078 maxDepth = Math.max(maxDepth, iInfo.depth);
19079 }
19080
19081 if (eInfo.depth <= maxDepth) {
19082 if (shifted[id]) {
19083 return null;
19084 }
19085
19086 changeDepth(ele, maxDepth + 1);
19087 shifted[id] = true;
19088 return true;
19089 }
19090
19091 return false;
19092 }; // for the directed case, try to make the edges all go down (i.e. depth i => depth i + 1)
19093
19094
19095 if (directed && maximal) {
19096 var Q = [];
19097 var shifted = {};
19098
19099 var enqueue = function enqueue(n) {
19100 return Q.push(n);
19101 };
19102
19103 var dequeue = function dequeue() {
19104 return Q.shift();
19105 };
19106
19107 nodes.forEach(function (n) {
19108 return Q.push(n);
19109 });
19110
19111 while (Q.length > 0) {
19112 var _ele3 = dequeue();
19113
19114 var didShift = adjustMaximally(_ele3, shifted);
19115
19116 if (didShift) {
19117 _ele3.outgoers().filter(function (el) {
19118 return el.isNode() && eles.has(el);
19119 }).forEach(enqueue);
19120 } else if (didShift === null) {
19121 warn('Detected double maximal shift for node `' + _ele3.id() + '`. Bailing maximal adjustment due to cycle. Use `options.maximal: true` only on DAGs.');
19122 break; // exit on failure
19123 }
19124 }
19125 }
19126
19127 assignDepths(); // clear holes
19128 // find min distance we need to leave between nodes
19129
19130 var minDistance = 0;
19131
19132 if (options.avoidOverlap) {
19133 for (var _i4 = 0; _i4 < nodes.length; _i4++) {
19134 var n = nodes[_i4];
19135 var nbb = n.layoutDimensions(options);
19136 var w = nbb.w;
19137 var h = nbb.h;
19138 minDistance = Math.max(minDistance, w, h);
19139 }
19140 } // get the weighted percent for an element based on its connectivity to other levels
19141
19142
19143 var cachedWeightedPercent = {};
19144
19145 var getWeightedPercent = function getWeightedPercent(ele) {
19146 if (cachedWeightedPercent[ele.id()]) {
19147 return cachedWeightedPercent[ele.id()];
19148 }
19149
19150 var eleDepth = getInfo(ele).depth;
19151 var neighbors = ele.neighborhood();
19152 var percent = 0;
19153 var samples = 0;
19154
19155 for (var _i5 = 0; _i5 < neighbors.length; _i5++) {
19156 var neighbor = neighbors[_i5];
19157
19158 if (neighbor.isEdge() || neighbor.isParent() || !nodes.has(neighbor)) {
19159 continue;
19160 }
19161
19162 var bf = getInfo(neighbor);
19163
19164 if (bf == null) {
19165 continue;
19166 }
19167
19168 var index = bf.index;
19169 var depth = bf.depth; // unassigned neighbours shouldn't affect the ordering
19170
19171 if (index == null || depth == null) {
19172 continue;
19173 }
19174
19175 var nDepth = depths[depth].length;
19176
19177 if (depth < eleDepth) {
19178 // only get influenced by elements above
19179 percent += index / nDepth;
19180 samples++;
19181 }
19182 }
19183
19184 samples = Math.max(1, samples);
19185 percent = percent / samples;
19186
19187 if (samples === 0) {
19188 // put lone nodes at the start
19189 percent = 0;
19190 }
19191
19192 cachedWeightedPercent[ele.id()] = percent;
19193 return percent;
19194 }; // rearrange the indices in each depth level based on connectivity
19195
19196
19197 var sortFn = function sortFn(a, b) {
19198 var apct = getWeightedPercent(a);
19199 var bpct = getWeightedPercent(b);
19200 var diff = apct - bpct;
19201
19202 if (diff === 0) {
19203 return ascending(a.id(), b.id()); // make sure sort doesn't have don't-care comparisons
19204 } else {
19205 return diff;
19206 }
19207 }; // sort each level to make connected nodes closer
19208
19209
19210 for (var _i6 = 0; _i6 < depths.length; _i6++) {
19211 depths[_i6].sort(sortFn);
19212
19213 assignDepthsAt(_i6);
19214 } // assign orphan nodes to a new top-level depth
19215
19216
19217 var orphanDepth = [];
19218
19219 for (var _i7 = 0; _i7 < orphanNodes.length; _i7++) {
19220 orphanDepth.push(orphanNodes[_i7]);
19221 }
19222
19223 depths.unshift(orphanDepth);
19224 assignDepths();
19225 var biggestDepthSize = 0;
19226
19227 for (var _i8 = 0; _i8 < depths.length; _i8++) {
19228 biggestDepthSize = Math.max(depths[_i8].length, biggestDepthSize);
19229 }
19230
19231 var center = {
19232 x: bb.x1 + bb.w / 2,
19233 y: bb.x1 + bb.h / 2
19234 };
19235 var maxDepthSize = depths.reduce(function (max, eles) {
19236 return Math.max(max, eles.length);
19237 }, 0);
19238
19239 var getPosition = function getPosition(ele) {
19240 var _getInfo2 = getInfo(ele),
19241 depth = _getInfo2.depth,
19242 index = _getInfo2.index;
19243
19244 var depthSize = depths[depth].length;
19245 var distanceX = Math.max(bb.w / ((options.grid ? maxDepthSize : depthSize) + 1), minDistance);
19246 var distanceY = Math.max(bb.h / (depths.length + 1), minDistance);
19247 var radiusStepSize = Math.min(bb.w / 2 / depths.length, bb.h / 2 / depths.length);
19248 radiusStepSize = Math.max(radiusStepSize, minDistance);
19249
19250 if (!options.circle) {
19251 var epos = {
19252 x: center.x + (index + 1 - (depthSize + 1) / 2) * distanceX,
19253 y: (depth + 1) * distanceY
19254 };
19255 return epos;
19256 } else {
19257 var radius = radiusStepSize * depth + radiusStepSize - (depths.length > 0 && depths[0].length <= 3 ? radiusStepSize / 2 : 0);
19258 var theta = 2 * Math.PI / depths[depth].length * index;
19259
19260 if (depth === 0 && depths[0].length === 1) {
19261 radius = 1;
19262 }
19263
19264 return {
19265 x: center.x + radius * Math.cos(theta),
19266 y: center.y + radius * Math.sin(theta)
19267 };
19268 }
19269 };
19270
19271 eles.nodes().layoutPositions(this, options, getPosition);
19272 return this; // chaining
19273};
19274
19275var defaults$a = {
19276 fit: true,
19277 // whether to fit the viewport to the graph
19278 padding: 30,
19279 // the padding on fit
19280 boundingBox: undefined,
19281 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19282 avoidOverlap: true,
19283 // prevents node overlap, may overflow boundingBox and radius if not enough space
19284 nodeDimensionsIncludeLabels: false,
19285 // Excludes the label when calculating node bounding boxes for the layout algorithm
19286 spacingFactor: undefined,
19287 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19288 radius: undefined,
19289 // the radius of the circle
19290 startAngle: 3 / 2 * Math.PI,
19291 // where nodes start in radians
19292 sweep: undefined,
19293 // how many radians should be between the first and last node (defaults to full circle)
19294 clockwise: true,
19295 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19296 sort: undefined,
19297 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
19298 animate: false,
19299 // whether to transition the node positions
19300 animationDuration: 500,
19301 // duration of animation in ms if enabled
19302 animationEasing: undefined,
19303 // easing of animation if enabled
19304 animateFilter: function animateFilter(node, i) {
19305 return true;
19306 },
19307 // 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
19308 ready: undefined,
19309 // callback on layoutready
19310 stop: undefined,
19311 // callback on layoutstop
19312 transform: function transform(node, position) {
19313 return position;
19314 } // transform a given node position. Useful for changing flow direction in discrete layouts
19315
19316};
19317
19318function CircleLayout(options) {
19319 this.options = extend({}, defaults$a, options);
19320}
19321
19322CircleLayout.prototype.run = function () {
19323 var params = this.options;
19324 var options = params;
19325 var cy = params.cy;
19326 var eles = options.eles;
19327 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19328 var nodes = eles.nodes().not(':parent');
19329
19330 if (options.sort) {
19331 nodes = nodes.sort(options.sort);
19332 }
19333
19334 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19335 x1: 0,
19336 y1: 0,
19337 w: cy.width(),
19338 h: cy.height()
19339 });
19340 var center = {
19341 x: bb.x1 + bb.w / 2,
19342 y: bb.y1 + bb.h / 2
19343 };
19344 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / nodes.length : options.sweep;
19345 var dTheta = sweep / Math.max(1, nodes.length - 1);
19346 var r;
19347 var minDistance = 0;
19348
19349 for (var i = 0; i < nodes.length; i++) {
19350 var n = nodes[i];
19351 var nbb = n.layoutDimensions(options);
19352 var w = nbb.w;
19353 var h = nbb.h;
19354 minDistance = Math.max(minDistance, w, h);
19355 }
19356
19357 if (number(options.radius)) {
19358 r = options.radius;
19359 } else if (nodes.length <= 1) {
19360 r = 0;
19361 } else {
19362 r = Math.min(bb.h, bb.w) / 2 - minDistance;
19363 } // calculate the radius
19364
19365
19366 if (nodes.length > 1 && options.avoidOverlap) {
19367 // but only if more than one node (can't overlap)
19368 minDistance *= 1.75; // just to have some nice spacing
19369
19370 var dcos = Math.cos(dTheta) - Math.cos(0);
19371 var dsin = Math.sin(dTheta) - Math.sin(0);
19372 var rMin = Math.sqrt(minDistance * minDistance / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19373
19374 r = Math.max(rMin, r);
19375 }
19376
19377 var getPos = function getPos(ele, i) {
19378 var theta = options.startAngle + i * dTheta * (clockwise ? 1 : -1);
19379 var rx = r * Math.cos(theta);
19380 var ry = r * Math.sin(theta);
19381 var pos = {
19382 x: center.x + rx,
19383 y: center.y + ry
19384 };
19385 return pos;
19386 };
19387
19388 eles.nodes().layoutPositions(this, options, getPos);
19389 return this; // chaining
19390};
19391
19392var defaults$b = {
19393 fit: true,
19394 // whether to fit the viewport to the graph
19395 padding: 30,
19396 // the padding on fit
19397 startAngle: 3 / 2 * Math.PI,
19398 // where nodes start in radians
19399 sweep: undefined,
19400 // how many radians should be between the first and last node (defaults to full circle)
19401 clockwise: true,
19402 // whether the layout should go clockwise (true) or counterclockwise/anticlockwise (false)
19403 equidistant: false,
19404 // whether levels have an equal radial distance betwen them, may cause bounding box overflow
19405 minNodeSpacing: 10,
19406 // min spacing between outside of nodes (used for radius adjustment)
19407 boundingBox: undefined,
19408 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19409 avoidOverlap: true,
19410 // prevents node overlap, may overflow boundingBox if not enough space
19411 nodeDimensionsIncludeLabels: false,
19412 // Excludes the label when calculating node bounding boxes for the layout algorithm
19413 height: undefined,
19414 // height of layout area (overrides container height)
19415 width: undefined,
19416 // width of layout area (overrides container width)
19417 spacingFactor: undefined,
19418 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
19419 concentric: function concentric(node) {
19420 // returns numeric value for each node, placing higher nodes in levels towards the centre
19421 return node.degree();
19422 },
19423 levelWidth: function levelWidth(nodes) {
19424 // the variation of concentric values in each level
19425 return nodes.maxDegree() / 4;
19426 },
19427 animate: false,
19428 // whether to transition the node positions
19429 animationDuration: 500,
19430 // duration of animation in ms if enabled
19431 animationEasing: undefined,
19432 // easing of animation if enabled
19433 animateFilter: function animateFilter(node, i) {
19434 return true;
19435 },
19436 // 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
19437 ready: undefined,
19438 // callback on layoutready
19439 stop: undefined,
19440 // callback on layoutstop
19441 transform: function transform(node, position) {
19442 return position;
19443 } // transform a given node position. Useful for changing flow direction in discrete layouts
19444
19445};
19446
19447function ConcentricLayout(options) {
19448 this.options = extend({}, defaults$b, options);
19449}
19450
19451ConcentricLayout.prototype.run = function () {
19452 var params = this.options;
19453 var options = params;
19454 var clockwise = options.counterclockwise !== undefined ? !options.counterclockwise : options.clockwise;
19455 var cy = params.cy;
19456 var eles = options.eles;
19457 var nodes = eles.nodes().not(':parent');
19458 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
19459 x1: 0,
19460 y1: 0,
19461 w: cy.width(),
19462 h: cy.height()
19463 });
19464 var center = {
19465 x: bb.x1 + bb.w / 2,
19466 y: bb.y1 + bb.h / 2
19467 };
19468 var nodeValues = []; // { node, value }
19469
19470 var maxNodeSize = 0;
19471
19472 for (var i = 0; i < nodes.length; i++) {
19473 var node = nodes[i];
19474 var value = void 0; // calculate the node value
19475
19476 value = options.concentric(node);
19477 nodeValues.push({
19478 value: value,
19479 node: node
19480 }); // for style mapping
19481
19482 node._private.scratch.concentric = value;
19483 } // in case we used the `concentric` in style
19484
19485
19486 nodes.updateStyle(); // calculate max size now based on potentially updated mappers
19487
19488 for (var _i = 0; _i < nodes.length; _i++) {
19489 var _node = nodes[_i];
19490
19491 var nbb = _node.layoutDimensions(options);
19492
19493 maxNodeSize = Math.max(maxNodeSize, nbb.w, nbb.h);
19494 } // sort node values in descreasing order
19495
19496
19497 nodeValues.sort(function (a, b) {
19498 return b.value - a.value;
19499 });
19500 var levelWidth = options.levelWidth(nodes); // put the values into levels
19501
19502 var levels = [[]];
19503 var currentLevel = levels[0];
19504
19505 for (var _i2 = 0; _i2 < nodeValues.length; _i2++) {
19506 var val = nodeValues[_i2];
19507
19508 if (currentLevel.length > 0) {
19509 var diff = Math.abs(currentLevel[0].value - val.value);
19510
19511 if (diff >= levelWidth) {
19512 currentLevel = [];
19513 levels.push(currentLevel);
19514 }
19515 }
19516
19517 currentLevel.push(val);
19518 } // create positions from levels
19519
19520
19521 var minDist = maxNodeSize + options.minNodeSpacing; // min dist between nodes
19522
19523 if (!options.avoidOverlap) {
19524 // then strictly constrain to bb
19525 var firstLvlHasMulti = levels.length > 0 && levels[0].length > 1;
19526 var maxR = Math.min(bb.w, bb.h) / 2 - minDist;
19527 var rStep = maxR / (levels.length + firstLvlHasMulti ? 1 : 0);
19528 minDist = Math.min(minDist, rStep);
19529 } // find the metrics for each level
19530
19531
19532 var r = 0;
19533
19534 for (var _i3 = 0; _i3 < levels.length; _i3++) {
19535 var level = levels[_i3];
19536 var sweep = options.sweep === undefined ? 2 * Math.PI - 2 * Math.PI / level.length : options.sweep;
19537 var dTheta = level.dTheta = sweep / Math.max(1, level.length - 1); // calculate the radius
19538
19539 if (level.length > 1 && options.avoidOverlap) {
19540 // but only if more than one node (can't overlap)
19541 var dcos = Math.cos(dTheta) - Math.cos(0);
19542 var dsin = Math.sin(dTheta) - Math.sin(0);
19543 var rMin = Math.sqrt(minDist * minDist / (dcos * dcos + dsin * dsin)); // s.t. no nodes overlapping
19544
19545 r = Math.max(rMin, r);
19546 }
19547
19548 level.r = r;
19549 r += minDist;
19550 }
19551
19552 if (options.equidistant) {
19553 var rDeltaMax = 0;
19554 var _r = 0;
19555
19556 for (var _i4 = 0; _i4 < levels.length; _i4++) {
19557 var _level = levels[_i4];
19558 var rDelta = _level.r - _r;
19559 rDeltaMax = Math.max(rDeltaMax, rDelta);
19560 }
19561
19562 _r = 0;
19563
19564 for (var _i5 = 0; _i5 < levels.length; _i5++) {
19565 var _level2 = levels[_i5];
19566
19567 if (_i5 === 0) {
19568 _r = _level2.r;
19569 }
19570
19571 _level2.r = _r;
19572 _r += rDeltaMax;
19573 }
19574 } // calculate the node positions
19575
19576
19577 var pos = {}; // id => position
19578
19579 for (var _i6 = 0; _i6 < levels.length; _i6++) {
19580 var _level3 = levels[_i6];
19581 var _dTheta = _level3.dTheta;
19582 var _r2 = _level3.r;
19583
19584 for (var j = 0; j < _level3.length; j++) {
19585 var _val = _level3[j];
19586 var theta = options.startAngle + (clockwise ? 1 : -1) * _dTheta * j;
19587 var p = {
19588 x: center.x + _r2 * Math.cos(theta),
19589 y: center.y + _r2 * Math.sin(theta)
19590 };
19591 pos[_val.node.id()] = p;
19592 }
19593 } // position the nodes
19594
19595
19596 eles.nodes().layoutPositions(this, options, function (ele) {
19597 var id = ele.id();
19598 return pos[id];
19599 });
19600 return this; // chaining
19601};
19602
19603/*
19604The CoSE layout was written by Gerardo Huck.
19605https://www.linkedin.com/in/gerardohuck/
19606
19607Based on the following article:
19608http://dl.acm.org/citation.cfm?id=1498047
19609
19610Modifications tracked on Github.
19611*/
19612var DEBUG;
19613/**
19614 * @brief : default layout options
19615 */
19616
19617var defaults$c = {
19618 // Called on `layoutready`
19619 ready: function ready() {},
19620 // Called on `layoutstop`
19621 stop: function stop() {},
19622 // Whether to animate while running the layout
19623 // true : Animate continuously as the layout is running
19624 // false : Just show the end result
19625 // 'end' : Animate with the end result, from the initial positions to the end positions
19626 animate: true,
19627 // Easing of the animation for animate:'end'
19628 animationEasing: undefined,
19629 // The duration of the animation for animate:'end'
19630 animationDuration: undefined,
19631 // A function that determines whether the node should be animated
19632 // All nodes animated by default on animate enabled
19633 // Non-animated nodes are positioned immediately when the layout starts
19634 animateFilter: function animateFilter(node, i) {
19635 return true;
19636 },
19637 // The layout animates only after this many milliseconds for animate:true
19638 // (prevents flashing on fast runs)
19639 animationThreshold: 250,
19640 // Number of iterations between consecutive screen positions update
19641 refresh: 20,
19642 // Whether to fit the network view after when done
19643 fit: true,
19644 // Padding on fit
19645 padding: 30,
19646 // Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
19647 boundingBox: undefined,
19648 // Excludes the label when calculating node bounding boxes for the layout algorithm
19649 nodeDimensionsIncludeLabels: false,
19650 // Randomize the initial positions of the nodes (true) or use existing positions (false)
19651 randomize: false,
19652 // Extra spacing between components in non-compound graphs
19653 componentSpacing: 40,
19654 // Node repulsion (non overlapping) multiplier
19655 nodeRepulsion: function nodeRepulsion(node) {
19656 return 2048;
19657 },
19658 // Node repulsion (overlapping) multiplier
19659 nodeOverlap: 4,
19660 // Ideal edge (non nested) length
19661 idealEdgeLength: function idealEdgeLength(edge) {
19662 return 32;
19663 },
19664 // Divisor to compute edge forces
19665 edgeElasticity: function edgeElasticity(edge) {
19666 return 32;
19667 },
19668 // Nesting factor (multiplier) to compute ideal edge length for nested edges
19669 nestingFactor: 1.2,
19670 // Gravity force (constant)
19671 gravity: 1,
19672 // Maximum number of iterations to perform
19673 numIter: 1000,
19674 // Initial temperature (maximum node displacement)
19675 initialTemp: 1000,
19676 // Cooling factor (how the temperature is reduced between consecutive iterations
19677 coolingFactor: 0.99,
19678 // Lower temperature threshold (below this point the layout will end)
19679 minTemp: 1.0
19680};
19681/**
19682 * @brief : constructor
19683 * @arg options : object containing layout options
19684 */
19685
19686function CoseLayout(options) {
19687 this.options = extend({}, defaults$c, options);
19688 this.options.layout = this;
19689}
19690/**
19691 * @brief : runs the layout
19692 */
19693
19694
19695CoseLayout.prototype.run = function () {
19696 var options = this.options;
19697 var cy = options.cy;
19698 var layout = this;
19699 layout.stopped = false;
19700
19701 if (options.animate === true || options.animate === false) {
19702 layout.emit({
19703 type: 'layoutstart',
19704 layout: layout
19705 });
19706 } // Set DEBUG - Global variable
19707
19708
19709 if (true === options.debug) {
19710 DEBUG = true;
19711 } else {
19712 DEBUG = false;
19713 } // Initialize layout info
19714
19715
19716 var layoutInfo = createLayoutInfo(cy, layout, options); // Show LayoutInfo contents if debugging
19717
19718 if (DEBUG) {
19719 printLayoutInfo(layoutInfo);
19720 } // If required, randomize node positions
19721
19722
19723 if (options.randomize) {
19724 randomizePositions(layoutInfo);
19725 }
19726
19727 var startTime = performanceNow();
19728
19729 var refresh = function refresh() {
19730 refreshPositions(layoutInfo, cy, options); // Fit the graph if necessary
19731
19732 if (true === options.fit) {
19733 cy.fit(options.padding);
19734 }
19735 };
19736
19737 var mainLoop = function mainLoop(i) {
19738 if (layout.stopped || i >= options.numIter) {
19739 // logDebug("Layout manually stopped. Stopping computation in step " + i);
19740 return false;
19741 } // Do one step in the phisical simulation
19742
19743
19744 step$1(layoutInfo, options); // Update temperature
19745
19746 layoutInfo.temperature = layoutInfo.temperature * options.coolingFactor; // logDebug("New temperature: " + layoutInfo.temperature);
19747
19748 if (layoutInfo.temperature < options.minTemp) {
19749 // logDebug("Temperature drop below minimum threshold. Stopping computation in step " + i);
19750 return false;
19751 }
19752
19753 return true;
19754 };
19755
19756 var done = function done() {
19757 if (options.animate === true || options.animate === false) {
19758 refresh(); // Layout has finished
19759
19760 layout.one('layoutstop', options.stop);
19761 layout.emit({
19762 type: 'layoutstop',
19763 layout: layout
19764 });
19765 } else {
19766 var nodes = options.eles.nodes();
19767 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
19768 nodes.layoutPositions(layout, options, getScaledPos);
19769 }
19770 };
19771
19772 var i = 0;
19773 var loopRet = true;
19774
19775 if (options.animate === true) {
19776 var frame = function frame() {
19777 var f = 0;
19778
19779 while (loopRet && f < options.refresh) {
19780 loopRet = mainLoop(i);
19781 i++;
19782 f++;
19783 }
19784
19785 if (!loopRet) {
19786 // it's done
19787 separateComponents(layoutInfo, options);
19788 done();
19789 } else {
19790 var now = performanceNow();
19791
19792 if (now - startTime >= options.animationThreshold) {
19793 refresh();
19794 }
19795
19796 requestAnimationFrame(frame);
19797 }
19798 };
19799
19800 frame();
19801 } else {
19802 while (loopRet) {
19803 loopRet = mainLoop(i);
19804 i++;
19805 }
19806
19807 separateComponents(layoutInfo, options);
19808 done();
19809 }
19810
19811 return this; // chaining
19812};
19813/**
19814 * @brief : called on continuous layouts to stop them before they finish
19815 */
19816
19817
19818CoseLayout.prototype.stop = function () {
19819 this.stopped = true;
19820
19821 if (this.thread) {
19822 this.thread.stop();
19823 }
19824
19825 this.emit('layoutstop');
19826 return this; // chaining
19827};
19828
19829CoseLayout.prototype.destroy = function () {
19830 if (this.thread) {
19831 this.thread.stop();
19832 }
19833
19834 return this; // chaining
19835};
19836/**
19837 * @brief : Creates an object which is contains all the data
19838 * used in the layout process
19839 * @arg cy : cytoscape.js object
19840 * @return : layoutInfo object initialized
19841 */
19842
19843
19844var createLayoutInfo = function createLayoutInfo(cy, layout, options) {
19845 // Shortcut
19846 var edges = options.eles.edges();
19847 var nodes = options.eles.nodes();
19848 var layoutInfo = {
19849 isCompound: cy.hasCompoundNodes(),
19850 layoutNodes: [],
19851 idToIndex: {},
19852 nodeSize: nodes.size(),
19853 graphSet: [],
19854 indexToGraph: [],
19855 layoutEdges: [],
19856 edgeSize: edges.size(),
19857 temperature: options.initialTemp,
19858 clientWidth: cy.width(),
19859 clientHeight: cy.width(),
19860 boundingBox: makeBoundingBox(options.boundingBox ? options.boundingBox : {
19861 x1: 0,
19862 y1: 0,
19863 w: cy.width(),
19864 h: cy.height()
19865 })
19866 };
19867 var components = options.eles.components();
19868 var id2cmptId = {};
19869
19870 for (var i = 0; i < components.length; i++) {
19871 var component = components[i];
19872
19873 for (var j = 0; j < component.length; j++) {
19874 var node = component[j];
19875 id2cmptId[node.id()] = i;
19876 }
19877 } // Iterate over all nodes, creating layout nodes
19878
19879
19880 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19881 var n = nodes[i];
19882 var nbb = n.layoutDimensions(options);
19883 var tempNode = {};
19884 tempNode.isLocked = n.locked();
19885 tempNode.id = n.data('id');
19886 tempNode.parentId = n.data('parent');
19887 tempNode.cmptId = id2cmptId[n.id()];
19888 tempNode.children = [];
19889 tempNode.positionX = n.position('x');
19890 tempNode.positionY = n.position('y');
19891 tempNode.offsetX = 0;
19892 tempNode.offsetY = 0;
19893 tempNode.height = nbb.w;
19894 tempNode.width = nbb.h;
19895 tempNode.maxX = tempNode.positionX + tempNode.width / 2;
19896 tempNode.minX = tempNode.positionX - tempNode.width / 2;
19897 tempNode.maxY = tempNode.positionY + tempNode.height / 2;
19898 tempNode.minY = tempNode.positionY - tempNode.height / 2;
19899 tempNode.padLeft = parseFloat(n.style('padding'));
19900 tempNode.padRight = parseFloat(n.style('padding'));
19901 tempNode.padTop = parseFloat(n.style('padding'));
19902 tempNode.padBottom = parseFloat(n.style('padding')); // forces
19903
19904 tempNode.nodeRepulsion = fn(options.nodeRepulsion) ? options.nodeRepulsion(n) : options.nodeRepulsion; // Add new node
19905
19906 layoutInfo.layoutNodes.push(tempNode); // Add entry to id-index map
19907
19908 layoutInfo.idToIndex[tempNode.id] = i;
19909 } // Inline implementation of a queue, used for traversing the graph in BFS order
19910
19911
19912 var queue = [];
19913 var start = 0; // Points to the start the queue
19914
19915 var end = -1; // Points to the end of the queue
19916
19917 var tempGraph = []; // Second pass to add child information and
19918 // initialize queue for hierarchical traversal
19919
19920 for (var i = 0; i < layoutInfo.nodeSize; i++) {
19921 var n = layoutInfo.layoutNodes[i];
19922 var p_id = n.parentId; // Check if node n has a parent node
19923
19924 if (null != p_id) {
19925 // Add node Id to parent's list of children
19926 layoutInfo.layoutNodes[layoutInfo.idToIndex[p_id]].children.push(n.id);
19927 } else {
19928 // If a node doesn't have a parent, then it's in the root graph
19929 queue[++end] = n.id;
19930 tempGraph.push(n.id);
19931 }
19932 } // Add root graph to graphSet
19933
19934
19935 layoutInfo.graphSet.push(tempGraph); // Traverse the graph, level by level,
19936
19937 while (start <= end) {
19938 // Get the node to visit and remove it from queue
19939 var node_id = queue[start++];
19940 var node_ix = layoutInfo.idToIndex[node_id];
19941 var node = layoutInfo.layoutNodes[node_ix];
19942 var children = node.children;
19943
19944 if (children.length > 0) {
19945 // Add children nodes as a new graph to graph set
19946 layoutInfo.graphSet.push(children); // Add children to que queue to be visited
19947
19948 for (var i = 0; i < children.length; i++) {
19949 queue[++end] = children[i];
19950 }
19951 }
19952 } // Create indexToGraph map
19953
19954
19955 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
19956 var graph = layoutInfo.graphSet[i];
19957
19958 for (var j = 0; j < graph.length; j++) {
19959 var index = layoutInfo.idToIndex[graph[j]];
19960 layoutInfo.indexToGraph[index] = i;
19961 }
19962 } // Iterate over all edges, creating Layout Edges
19963
19964
19965 for (var i = 0; i < layoutInfo.edgeSize; i++) {
19966 var e = edges[i];
19967 var tempEdge = {};
19968 tempEdge.id = e.data('id');
19969 tempEdge.sourceId = e.data('source');
19970 tempEdge.targetId = e.data('target'); // Compute ideal length
19971
19972 var idealLength = fn(options.idealEdgeLength) ? options.idealEdgeLength(e) : options.idealEdgeLength;
19973 var elasticity = fn(options.edgeElasticity) ? options.edgeElasticity(e) : options.edgeElasticity; // Check if it's an inter graph edge
19974
19975 var sourceIx = layoutInfo.idToIndex[tempEdge.sourceId];
19976 var targetIx = layoutInfo.idToIndex[tempEdge.targetId];
19977 var sourceGraph = layoutInfo.indexToGraph[sourceIx];
19978 var targetGraph = layoutInfo.indexToGraph[targetIx];
19979
19980 if (sourceGraph != targetGraph) {
19981 // Find lowest common graph ancestor
19982 var lca = findLCA(tempEdge.sourceId, tempEdge.targetId, layoutInfo); // Compute sum of node depths, relative to lca graph
19983
19984 var lcaGraph = layoutInfo.graphSet[lca];
19985 var depth = 0; // Source depth
19986
19987 var tempNode = layoutInfo.layoutNodes[sourceIx];
19988
19989 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19990 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19991 depth++;
19992 } // Target depth
19993
19994
19995 tempNode = layoutInfo.layoutNodes[targetIx];
19996
19997 while (-1 === lcaGraph.indexOf(tempNode.id)) {
19998 tempNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[tempNode.parentId]];
19999 depth++;
20000 } // logDebug('LCA of nodes ' + tempEdge.sourceId + ' and ' + tempEdge.targetId +
20001 // ". Index: " + lca + " Contents: " + lcaGraph.toString() +
20002 // ". Depth: " + depth);
20003 // Update idealLength
20004
20005
20006 idealLength *= depth * options.nestingFactor;
20007 }
20008
20009 tempEdge.idealLength = idealLength;
20010 tempEdge.elasticity = elasticity;
20011 layoutInfo.layoutEdges.push(tempEdge);
20012 } // Finally, return layoutInfo object
20013
20014
20015 return layoutInfo;
20016};
20017/**
20018 * @brief : This function finds the index of the lowest common
20019 * graph ancestor between 2 nodes in the subtree
20020 * (from the graph hierarchy induced tree) whose
20021 * root is graphIx
20022 *
20023 * @arg node1: node1's ID
20024 * @arg node2: node2's ID
20025 * @arg layoutInfo: layoutInfo object
20026 *
20027 */
20028
20029
20030var findLCA = function findLCA(node1, node2, layoutInfo) {
20031 // Find their common ancester, starting from the root graph
20032 var res = findLCA_aux(node1, node2, 0, layoutInfo);
20033
20034 if (2 > res.count) {
20035 // If aux function couldn't find the common ancester,
20036 // then it is the root graph
20037 return 0;
20038 } else {
20039 return res.graph;
20040 }
20041};
20042/**
20043 * @brief : Auxiliary function used for LCA computation
20044 *
20045 * @arg node1 : node1's ID
20046 * @arg node2 : node2's ID
20047 * @arg graphIx : subgraph index
20048 * @arg layoutInfo : layoutInfo object
20049 *
20050 * @return : object of the form {count: X, graph: Y}, where:
20051 * X is the number of ancesters (max: 2) found in
20052 * graphIx (and it's subgraphs),
20053 * Y is the graph index of the lowest graph containing
20054 * all X nodes
20055 */
20056
20057
20058var findLCA_aux = function findLCA_aux(node1, node2, graphIx, layoutInfo) {
20059 var graph = layoutInfo.graphSet[graphIx]; // If both nodes belongs to graphIx
20060
20061 if (-1 < graph.indexOf(node1) && -1 < graph.indexOf(node2)) {
20062 return {
20063 count: 2,
20064 graph: graphIx
20065 };
20066 } // Make recursive calls for all subgraphs
20067
20068
20069 var c = 0;
20070
20071 for (var i = 0; i < graph.length; i++) {
20072 var nodeId = graph[i];
20073 var nodeIx = layoutInfo.idToIndex[nodeId];
20074 var children = layoutInfo.layoutNodes[nodeIx].children; // If the node has no child, skip it
20075
20076 if (0 === children.length) {
20077 continue;
20078 }
20079
20080 var childGraphIx = layoutInfo.indexToGraph[layoutInfo.idToIndex[children[0]]];
20081 var result = findLCA_aux(node1, node2, childGraphIx, layoutInfo);
20082
20083 if (0 === result.count) {
20084 // Neither node1 nor node2 are present in this subgraph
20085 continue;
20086 } else if (1 === result.count) {
20087 // One of (node1, node2) is present in this subgraph
20088 c++;
20089
20090 if (2 === c) {
20091 // We've already found both nodes, no need to keep searching
20092 break;
20093 }
20094 } else {
20095 // Both nodes are present in this subgraph
20096 return result;
20097 }
20098 }
20099
20100 return {
20101 count: c,
20102 graph: graphIx
20103 };
20104};
20105/**
20106 * @brief: printsLayoutInfo into js console
20107 * Only used for debbuging
20108 */
20109
20110
20111if (false) {
20112 var printLayoutInfo;
20113}
20114/**
20115 * @brief : Randomizes the position of all nodes
20116 */
20117
20118
20119var randomizePositions = function randomizePositions(layoutInfo, cy) {
20120 var width = layoutInfo.clientWidth;
20121 var height = layoutInfo.clientHeight;
20122
20123 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20124 var n = layoutInfo.layoutNodes[i]; // No need to randomize compound nodes or locked nodes
20125
20126 if (0 === n.children.length && !n.isLocked) {
20127 n.positionX = Math.random() * width;
20128 n.positionY = Math.random() * height;
20129 }
20130 }
20131};
20132
20133var getScaleInBoundsFn = function getScaleInBoundsFn(layoutInfo, options, nodes) {
20134 var bb = layoutInfo.boundingBox;
20135 var coseBB = {
20136 x1: Infinity,
20137 x2: -Infinity,
20138 y1: Infinity,
20139 y2: -Infinity
20140 };
20141
20142 if (options.boundingBox) {
20143 nodes.forEach(function (node) {
20144 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[node.data('id')]];
20145 coseBB.x1 = Math.min(coseBB.x1, lnode.positionX);
20146 coseBB.x2 = Math.max(coseBB.x2, lnode.positionX);
20147 coseBB.y1 = Math.min(coseBB.y1, lnode.positionY);
20148 coseBB.y2 = Math.max(coseBB.y2, lnode.positionY);
20149 });
20150 coseBB.w = coseBB.x2 - coseBB.x1;
20151 coseBB.h = coseBB.y2 - coseBB.y1;
20152 }
20153
20154 return function (ele, i) {
20155 var lnode = layoutInfo.layoutNodes[layoutInfo.idToIndex[ele.data('id')]];
20156
20157 if (options.boundingBox) {
20158 // then add extra bounding box constraint
20159 var pctX = (lnode.positionX - coseBB.x1) / coseBB.w;
20160 var pctY = (lnode.positionY - coseBB.y1) / coseBB.h;
20161 return {
20162 x: bb.x1 + pctX * bb.w,
20163 y: bb.y1 + pctY * bb.h
20164 };
20165 } else {
20166 return {
20167 x: lnode.positionX,
20168 y: lnode.positionY
20169 };
20170 }
20171 };
20172};
20173/**
20174 * @brief : Updates the positions of nodes in the network
20175 * @arg layoutInfo : LayoutInfo object
20176 * @arg cy : Cytoscape object
20177 * @arg options : Layout options
20178 */
20179
20180
20181var refreshPositions = function refreshPositions(layoutInfo, cy, options) {
20182 // var s = 'Refreshing positions';
20183 // logDebug(s);
20184 var layout = options.layout;
20185 var nodes = options.eles.nodes();
20186 var getScaledPos = getScaleInBoundsFn(layoutInfo, options, nodes);
20187 nodes.positions(getScaledPos); // Trigger layoutReady only on first call
20188
20189 if (true !== layoutInfo.ready) {
20190 // s = 'Triggering layoutready';
20191 // logDebug(s);
20192 layoutInfo.ready = true;
20193 layout.one('layoutready', options.ready);
20194 layout.emit({
20195 type: 'layoutready',
20196 layout: this
20197 });
20198 }
20199};
20200/**
20201 * @brief : Logs a debug message in JS console, if DEBUG is ON
20202 */
20203// var logDebug = function(text) {
20204// if (DEBUG) {
20205// console.debug(text);
20206// }
20207// };
20208
20209/**
20210 * @brief : Performs one iteration of the physical simulation
20211 * @arg layoutInfo : LayoutInfo object already initialized
20212 * @arg cy : Cytoscape object
20213 * @arg options : Layout options
20214 */
20215
20216
20217var step$1 = function step(layoutInfo, options, _step) {
20218 // var s = "\n\n###############################";
20219 // s += "\nSTEP: " + step;
20220 // s += "\n###############################\n";
20221 // logDebug(s);
20222 // Calculate node repulsions
20223 calculateNodeForces(layoutInfo, options); // Calculate edge forces
20224
20225 calculateEdgeForces(layoutInfo); // Calculate gravity forces
20226
20227 calculateGravityForces(layoutInfo, options); // Propagate forces from parent to child
20228
20229 propagateForces(layoutInfo); // Update positions based on calculated forces
20230
20231 updatePositions(layoutInfo);
20232};
20233/**
20234 * @brief : Computes the node repulsion forces
20235 */
20236
20237
20238var calculateNodeForces = function calculateNodeForces(layoutInfo, options) {
20239 // Go through each of the graphs in graphSet
20240 // Nodes only repel each other if they belong to the same graph
20241 // var s = 'calculateNodeForces';
20242 // logDebug(s);
20243 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20244 var graph = layoutInfo.graphSet[i];
20245 var numNodes = graph.length; // s = "Set: " + graph.toString();
20246 // logDebug(s);
20247 // Now get all the pairs of nodes
20248 // Only get each pair once, (A, B) = (B, A)
20249
20250 for (var j = 0; j < numNodes; j++) {
20251 var node1 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]];
20252
20253 for (var k = j + 1; k < numNodes; k++) {
20254 var node2 = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[k]]];
20255 nodeRepulsion(node1, node2, layoutInfo, options);
20256 }
20257 }
20258 }
20259};
20260
20261var randomDistance = function randomDistance(max) {
20262 return -max + 2 * max * Math.random();
20263};
20264/**
20265 * @brief : Compute the node repulsion forces between a pair of nodes
20266 */
20267
20268
20269var nodeRepulsion = function nodeRepulsion(node1, node2, layoutInfo, options) {
20270 // var s = "Node repulsion. Node1: " + node1.id + " Node2: " + node2.id;
20271 var cmptId1 = node1.cmptId;
20272 var cmptId2 = node2.cmptId;
20273
20274 if (cmptId1 !== cmptId2 && !layoutInfo.isCompound) {
20275 return;
20276 } // Get direction of line connecting both node centers
20277
20278
20279 var directionX = node2.positionX - node1.positionX;
20280 var directionY = node2.positionY - node1.positionY;
20281 var maxRandDist = 1; // s += "\ndirectionX: " + directionX + ", directionY: " + directionY;
20282 // If both centers are the same, apply a random force
20283
20284 if (0 === directionX && 0 === directionY) {
20285 directionX = randomDistance(maxRandDist);
20286 directionY = randomDistance(maxRandDist);
20287 }
20288
20289 var overlap = nodesOverlap(node1, node2, directionX, directionY);
20290
20291 if (overlap > 0) {
20292 // s += "\nNodes DO overlap.";
20293 // s += "\nOverlap: " + overlap;
20294 // If nodes overlap, repulsion force is proportional
20295 // to the overlap
20296 var force = options.nodeOverlap * overlap; // Compute the module and components of the force vector
20297
20298 var distance = Math.sqrt(directionX * directionX + directionY * directionY); // s += "\nDistance: " + distance;
20299
20300 var forceX = force * directionX / distance;
20301 var forceY = force * directionY / distance;
20302 } else {
20303 // s += "\nNodes do NOT overlap.";
20304 // If there's no overlap, force is inversely proportional
20305 // to squared distance
20306 // Get clipping points for both nodes
20307 var point1 = findClippingPoint(node1, directionX, directionY);
20308 var point2 = findClippingPoint(node2, -1 * directionX, -1 * directionY); // Use clipping points to compute distance
20309
20310 var distanceX = point2.x - point1.x;
20311 var distanceY = point2.y - point1.y;
20312 var distanceSqr = distanceX * distanceX + distanceY * distanceY;
20313 var distance = Math.sqrt(distanceSqr); // s += "\nDistance: " + distance;
20314 // Compute the module and components of the force vector
20315
20316 var force = (node1.nodeRepulsion + node2.nodeRepulsion) / distanceSqr;
20317 var forceX = force * distanceX / distance;
20318 var forceY = force * distanceY / distance;
20319 } // Apply force
20320
20321
20322 if (!node1.isLocked) {
20323 node1.offsetX -= forceX;
20324 node1.offsetY -= forceY;
20325 }
20326
20327 if (!node2.isLocked) {
20328 node2.offsetX += forceX;
20329 node2.offsetY += forceY;
20330 } // s += "\nForceX: " + forceX + " ForceY: " + forceY;
20331 // logDebug(s);
20332
20333
20334 return;
20335};
20336/**
20337 * @brief : Determines whether two nodes overlap or not
20338 * @return : Amount of overlapping (0 => no overlap)
20339 */
20340
20341
20342var nodesOverlap = function nodesOverlap(node1, node2, dX, dY) {
20343 if (dX > 0) {
20344 var overlapX = node1.maxX - node2.minX;
20345 } else {
20346 var overlapX = node2.maxX - node1.minX;
20347 }
20348
20349 if (dY > 0) {
20350 var overlapY = node1.maxY - node2.minY;
20351 } else {
20352 var overlapY = node2.maxY - node1.minY;
20353 }
20354
20355 if (overlapX >= 0 && overlapY >= 0) {
20356 return Math.sqrt(overlapX * overlapX + overlapY * overlapY);
20357 } else {
20358 return 0;
20359 }
20360};
20361/**
20362 * @brief : Finds the point in which an edge (direction dX, dY) intersects
20363 * the rectangular bounding box of it's source/target node
20364 */
20365
20366
20367var findClippingPoint = function findClippingPoint(node, dX, dY) {
20368 // Shorcuts
20369 var X = node.positionX;
20370 var Y = node.positionY;
20371 var H = node.height || 1;
20372 var W = node.width || 1;
20373 var dirSlope = dY / dX;
20374 var nodeSlope = H / W; // var s = 'Computing clipping point of node ' + node.id +
20375 // " . Height: " + H + ", Width: " + W +
20376 // "\nDirection " + dX + ", " + dY;
20377 //
20378 // Compute intersection
20379
20380 var res = {}; // Case: Vertical direction (up)
20381
20382 if (0 === dX && 0 < dY) {
20383 res.x = X; // s += "\nUp direction";
20384
20385 res.y = Y + H / 2;
20386 return res;
20387 } // Case: Vertical direction (down)
20388
20389
20390 if (0 === dX && 0 > dY) {
20391 res.x = X;
20392 res.y = Y + H / 2; // s += "\nDown direction";
20393
20394 return res;
20395 } // Case: Intersects the right border
20396
20397
20398 if (0 < dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20399 res.x = X + W / 2;
20400 res.y = Y + W * dY / 2 / dX; // s += "\nRightborder";
20401
20402 return res;
20403 } // Case: Intersects the left border
20404
20405
20406 if (0 > dX && -1 * nodeSlope <= dirSlope && dirSlope <= nodeSlope) {
20407 res.x = X - W / 2;
20408 res.y = Y - W * dY / 2 / dX; // s += "\nLeftborder";
20409
20410 return res;
20411 } // Case: Intersects the top border
20412
20413
20414 if (0 < dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20415 res.x = X + H * dX / 2 / dY;
20416 res.y = Y + H / 2; // s += "\nTop border";
20417
20418 return res;
20419 } // Case: Intersects the bottom border
20420
20421
20422 if (0 > dY && (dirSlope <= -1 * nodeSlope || dirSlope >= nodeSlope)) {
20423 res.x = X - H * dX / 2 / dY;
20424 res.y = Y - H / 2; // s += "\nBottom border";
20425
20426 return res;
20427 } // s += "\nClipping point found at " + res.x + ", " + res.y;
20428 // logDebug(s);
20429
20430
20431 return res;
20432};
20433/**
20434 * @brief : Calculates all edge forces
20435 */
20436
20437
20438var calculateEdgeForces = function calculateEdgeForces(layoutInfo, options) {
20439 // Iterate over all edges
20440 for (var i = 0; i < layoutInfo.edgeSize; i++) {
20441 // Get edge, source & target nodes
20442 var edge = layoutInfo.layoutEdges[i];
20443 var sourceIx = layoutInfo.idToIndex[edge.sourceId];
20444 var source = layoutInfo.layoutNodes[sourceIx];
20445 var targetIx = layoutInfo.idToIndex[edge.targetId];
20446 var target = layoutInfo.layoutNodes[targetIx]; // Get direction of line connecting both node centers
20447
20448 var directionX = target.positionX - source.positionX;
20449 var directionY = target.positionY - source.positionY; // If both centers are the same, do nothing.
20450 // A random force has already been applied as node repulsion
20451
20452 if (0 === directionX && 0 === directionY) {
20453 continue;
20454 } // Get clipping points for both nodes
20455
20456
20457 var point1 = findClippingPoint(source, directionX, directionY);
20458 var point2 = findClippingPoint(target, -1 * directionX, -1 * directionY);
20459 var lx = point2.x - point1.x;
20460 var ly = point2.y - point1.y;
20461 var l = Math.sqrt(lx * lx + ly * ly);
20462 var force = Math.pow(edge.idealLength - l, 2) / edge.elasticity;
20463
20464 if (0 !== l) {
20465 var forceX = force * lx / l;
20466 var forceY = force * ly / l;
20467 } else {
20468 var forceX = 0;
20469 var forceY = 0;
20470 } // Add this force to target and source nodes
20471
20472
20473 if (!source.isLocked) {
20474 source.offsetX += forceX;
20475 source.offsetY += forceY;
20476 }
20477
20478 if (!target.isLocked) {
20479 target.offsetX -= forceX;
20480 target.offsetY -= forceY;
20481 } // var s = 'Edge force between nodes ' + source.id + ' and ' + target.id;
20482 // s += "\nDistance: " + l + " Force: (" + forceX + ", " + forceY + ")";
20483 // logDebug(s);
20484
20485 }
20486};
20487/**
20488 * @brief : Computes gravity forces for all nodes
20489 */
20490
20491
20492var calculateGravityForces = function calculateGravityForces(layoutInfo, options) {
20493 var distThreshold = 1; // var s = 'calculateGravityForces';
20494 // logDebug(s);
20495
20496 for (var i = 0; i < layoutInfo.graphSet.length; i++) {
20497 var graph = layoutInfo.graphSet[i];
20498 var numNodes = graph.length; // s = "Set: " + graph.toString();
20499 // logDebug(s);
20500 // Compute graph center
20501
20502 if (0 === i) {
20503 var centerX = layoutInfo.clientHeight / 2;
20504 var centerY = layoutInfo.clientWidth / 2;
20505 } else {
20506 // Get Parent node for this graph, and use its position as center
20507 var temp = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[0]]];
20508 var parent = layoutInfo.layoutNodes[layoutInfo.idToIndex[temp.parentId]];
20509 var centerX = parent.positionX;
20510 var centerY = parent.positionY;
20511 } // s = "Center found at: " + centerX + ", " + centerY;
20512 // logDebug(s);
20513 // Apply force to all nodes in graph
20514
20515
20516 for (var j = 0; j < numNodes; j++) {
20517 var node = layoutInfo.layoutNodes[layoutInfo.idToIndex[graph[j]]]; // s = "Node: " + node.id;
20518
20519 if (node.isLocked) {
20520 continue;
20521 }
20522
20523 var dx = centerX - node.positionX;
20524 var dy = centerY - node.positionY;
20525 var d = Math.sqrt(dx * dx + dy * dy);
20526
20527 if (d > distThreshold) {
20528 var fx = options.gravity * dx / d;
20529 var fy = options.gravity * dy / d;
20530 node.offsetX += fx;
20531 node.offsetY += fy; // s += ": Applied force: " + fx + ", " + fy;
20532 } // s += ": skypped since it's too close to center";
20533 // logDebug(s);
20534
20535 }
20536 }
20537};
20538/**
20539 * @brief : This function propagates the existing offsets from
20540 * parent nodes to its descendents.
20541 * @arg layoutInfo : layoutInfo Object
20542 * @arg cy : cytoscape Object
20543 * @arg options : Layout options
20544 */
20545
20546
20547var propagateForces = function propagateForces(layoutInfo, options) {
20548 // Inline implementation of a queue, used for traversing the graph in BFS order
20549 var queue = [];
20550 var start = 0; // Points to the start the queue
20551
20552 var end = -1; // Points to the end of the queue
20553 // logDebug('propagateForces');
20554 // Start by visiting the nodes in the root graph
20555
20556 queue.push.apply(queue, layoutInfo.graphSet[0]);
20557 end += layoutInfo.graphSet[0].length; // Traverse the graph, level by level,
20558
20559 while (start <= end) {
20560 // Get the node to visit and remove it from queue
20561 var nodeId = queue[start++];
20562 var nodeIndex = layoutInfo.idToIndex[nodeId];
20563 var node = layoutInfo.layoutNodes[nodeIndex];
20564 var children = node.children; // We only need to process the node if it's compound
20565
20566 if (0 < children.length && !node.isLocked) {
20567 var offX = node.offsetX;
20568 var offY = node.offsetY; // var s = "Propagating offset from parent node : " + node.id +
20569 // ". OffsetX: " + offX + ". OffsetY: " + offY;
20570 // s += "\n Children: " + children.toString();
20571 // logDebug(s);
20572
20573 for (var i = 0; i < children.length; i++) {
20574 var childNode = layoutInfo.layoutNodes[layoutInfo.idToIndex[children[i]]]; // Propagate offset
20575
20576 childNode.offsetX += offX;
20577 childNode.offsetY += offY; // Add children to queue to be visited
20578
20579 queue[++end] = children[i];
20580 } // Reset parent offsets
20581
20582
20583 node.offsetX = 0;
20584 node.offsetY = 0;
20585 }
20586 }
20587};
20588/**
20589 * @brief : Updates the layout model positions, based on
20590 * the accumulated forces
20591 */
20592
20593
20594var updatePositions = function updatePositions(layoutInfo, options) {
20595 // var s = 'Updating positions';
20596 // logDebug(s);
20597 // Reset boundaries for compound nodes
20598 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20599 var n = layoutInfo.layoutNodes[i];
20600
20601 if (0 < n.children.length) {
20602 // logDebug("Resetting boundaries of compound node: " + n.id);
20603 n.maxX = undefined;
20604 n.minX = undefined;
20605 n.maxY = undefined;
20606 n.minY = undefined;
20607 }
20608 }
20609
20610 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20611 var n = layoutInfo.layoutNodes[i];
20612
20613 if (0 < n.children.length || n.isLocked) {
20614 // No need to set compound or locked node position
20615 // logDebug("Skipping position update of node: " + n.id);
20616 continue;
20617 } // s = "Node: " + n.id + " Previous position: (" +
20618 // n.positionX + ", " + n.positionY + ").";
20619 // Limit displacement in order to improve stability
20620
20621
20622 var tempForce = limitForce(n.offsetX, n.offsetY, layoutInfo.temperature);
20623 n.positionX += tempForce.x;
20624 n.positionY += tempForce.y;
20625 n.offsetX = 0;
20626 n.offsetY = 0;
20627 n.minX = n.positionX - n.width;
20628 n.maxX = n.positionX + n.width;
20629 n.minY = n.positionY - n.height;
20630 n.maxY = n.positionY + n.height; // s += " New Position: (" + n.positionX + ", " + n.positionY + ").";
20631 // logDebug(s);
20632 // Update ancestry boudaries
20633
20634 updateAncestryBoundaries(n, layoutInfo);
20635 } // Update size, position of compund nodes
20636
20637
20638 for (var i = 0; i < layoutInfo.nodeSize; i++) {
20639 var n = layoutInfo.layoutNodes[i];
20640
20641 if (0 < n.children.length && !n.isLocked) {
20642 n.positionX = (n.maxX + n.minX) / 2;
20643 n.positionY = (n.maxY + n.minY) / 2;
20644 n.width = n.maxX - n.minX;
20645 n.height = n.maxY - n.minY; // s = "Updating position, size of compound node " + n.id;
20646 // s += "\nPositionX: " + n.positionX + ", PositionY: " + n.positionY;
20647 // s += "\nWidth: " + n.width + ", Height: " + n.height;
20648 // logDebug(s);
20649 }
20650 }
20651};
20652/**
20653 * @brief : Limits a force (forceX, forceY) to be not
20654 * greater (in modulo) than max.
20655 8 Preserves force direction.
20656 */
20657
20658
20659var limitForce = function limitForce(forceX, forceY, max) {
20660 // var s = "Limiting force: (" + forceX + ", " + forceY + "). Max: " + max;
20661 var force = Math.sqrt(forceX * forceX + forceY * forceY);
20662
20663 if (force > max) {
20664 var res = {
20665 x: max * forceX / force,
20666 y: max * forceY / force
20667 };
20668 } else {
20669 var res = {
20670 x: forceX,
20671 y: forceY
20672 };
20673 } // s += ".\nResult: (" + res.x + ", " + res.y + ")";
20674 // logDebug(s);
20675
20676
20677 return res;
20678};
20679/**
20680 * @brief : Function used for keeping track of compound node
20681 * sizes, since they should bound all their subnodes.
20682 */
20683
20684
20685var updateAncestryBoundaries = function updateAncestryBoundaries(node, layoutInfo) {
20686 // var s = "Propagating new position/size of node " + node.id;
20687 var parentId = node.parentId;
20688
20689 if (null == parentId) {
20690 // If there's no parent, we are done
20691 // s += ". No parent node.";
20692 // logDebug(s);
20693 return;
20694 } // Get Parent Node
20695
20696
20697 var p = layoutInfo.layoutNodes[layoutInfo.idToIndex[parentId]];
20698 var flag = false; // MaxX
20699
20700 if (null == p.maxX || node.maxX + p.padRight > p.maxX) {
20701 p.maxX = node.maxX + p.padRight;
20702 flag = true; // s += "\nNew maxX for parent node " + p.id + ": " + p.maxX;
20703 } // MinX
20704
20705
20706 if (null == p.minX || node.minX - p.padLeft < p.minX) {
20707 p.minX = node.minX - p.padLeft;
20708 flag = true; // s += "\nNew minX for parent node " + p.id + ": " + p.minX;
20709 } // MaxY
20710
20711
20712 if (null == p.maxY || node.maxY + p.padBottom > p.maxY) {
20713 p.maxY = node.maxY + p.padBottom;
20714 flag = true; // s += "\nNew maxY for parent node " + p.id + ": " + p.maxY;
20715 } // MinY
20716
20717
20718 if (null == p.minY || node.minY - p.padTop < p.minY) {
20719 p.minY = node.minY - p.padTop;
20720 flag = true; // s += "\nNew minY for parent node " + p.id + ": " + p.minY;
20721 } // If updated boundaries, propagate changes upward
20722
20723
20724 if (flag) {
20725 // logDebug(s);
20726 return updateAncestryBoundaries(p, layoutInfo);
20727 } // s += ". No changes in boundaries/position of parent node " + p.id;
20728 // logDebug(s);
20729
20730
20731 return;
20732};
20733
20734var separateComponents = function separateComponents(layoutInfo, options) {
20735 var nodes = layoutInfo.layoutNodes;
20736 var components = [];
20737
20738 for (var i = 0; i < nodes.length; i++) {
20739 var node = nodes[i];
20740 var cid = node.cmptId;
20741 var component = components[cid] = components[cid] || [];
20742 component.push(node);
20743 }
20744
20745 var totalA = 0;
20746
20747 for (var i = 0; i < components.length; i++) {
20748 var c = components[i];
20749
20750 if (!c) {
20751 continue;
20752 }
20753
20754 c.x1 = Infinity;
20755 c.x2 = -Infinity;
20756 c.y1 = Infinity;
20757 c.y2 = -Infinity;
20758
20759 for (var j = 0; j < c.length; j++) {
20760 var n = c[j];
20761 c.x1 = Math.min(c.x1, n.positionX - n.width / 2);
20762 c.x2 = Math.max(c.x2, n.positionX + n.width / 2);
20763 c.y1 = Math.min(c.y1, n.positionY - n.height / 2);
20764 c.y2 = Math.max(c.y2, n.positionY + n.height / 2);
20765 }
20766
20767 c.w = c.x2 - c.x1;
20768 c.h = c.y2 - c.y1;
20769 totalA += c.w * c.h;
20770 }
20771
20772 components.sort(function (c1, c2) {
20773 return c2.w * c2.h - c1.w * c1.h;
20774 });
20775 var x = 0;
20776 var y = 0;
20777 var usedW = 0;
20778 var rowH = 0;
20779 var maxRowW = Math.sqrt(totalA) * layoutInfo.clientWidth / layoutInfo.clientHeight;
20780
20781 for (var i = 0; i < components.length; i++) {
20782 var c = components[i];
20783
20784 if (!c) {
20785 continue;
20786 }
20787
20788 for (var j = 0; j < c.length; j++) {
20789 var n = c[j];
20790
20791 if (!n.isLocked) {
20792 n.positionX += x - c.x1;
20793 n.positionY += y - c.y1;
20794 }
20795 }
20796
20797 x += c.w + options.componentSpacing;
20798 usedW += c.w + options.componentSpacing;
20799 rowH = Math.max(rowH, c.h);
20800
20801 if (usedW > maxRowW) {
20802 y += rowH + options.componentSpacing;
20803 x = 0;
20804 usedW = 0;
20805 rowH = 0;
20806 }
20807 }
20808};
20809
20810var defaults$d = {
20811 fit: true,
20812 // whether to fit the viewport to the graph
20813 padding: 30,
20814 // padding used on fit
20815 boundingBox: undefined,
20816 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
20817 avoidOverlap: true,
20818 // prevents node overlap, may overflow boundingBox if not enough space
20819 avoidOverlapPadding: 10,
20820 // extra spacing around nodes when avoidOverlap: true
20821 nodeDimensionsIncludeLabels: false,
20822 // Excludes the label when calculating node bounding boxes for the layout algorithm
20823 spacingFactor: undefined,
20824 // Applies a multiplicative factor (>0) to expand or compress the overall area that the nodes take up
20825 condense: false,
20826 // uses all available space on false, uses minimal space on true
20827 rows: undefined,
20828 // force num of rows in the grid
20829 cols: undefined,
20830 // force num of columns in the grid
20831 position: function position(node) {},
20832 // returns { row, col } for element
20833 sort: undefined,
20834 // a sorting function to order the nodes; e.g. function(a, b){ return a.data('weight') - b.data('weight') }
20835 animate: false,
20836 // whether to transition the node positions
20837 animationDuration: 500,
20838 // duration of animation in ms if enabled
20839 animationEasing: undefined,
20840 // easing of animation if enabled
20841 animateFilter: function animateFilter(node, i) {
20842 return true;
20843 },
20844 // 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
20845 ready: undefined,
20846 // callback on layoutready
20847 stop: undefined,
20848 // callback on layoutstop
20849 transform: function transform(node, position) {
20850 return position;
20851 } // transform a given node position. Useful for changing flow direction in discrete layouts
20852
20853};
20854
20855function GridLayout(options) {
20856 this.options = extend({}, defaults$d, options);
20857}
20858
20859GridLayout.prototype.run = function () {
20860 var params = this.options;
20861 var options = params;
20862 var cy = params.cy;
20863 var eles = options.eles;
20864 var nodes = eles.nodes().not(':parent');
20865
20866 if (options.sort) {
20867 nodes = nodes.sort(options.sort);
20868 }
20869
20870 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
20871 x1: 0,
20872 y1: 0,
20873 w: cy.width(),
20874 h: cy.height()
20875 });
20876
20877 if (bb.h === 0 || bb.w === 0) {
20878 eles.nodes().layoutPositions(this, options, function (ele) {
20879 return {
20880 x: bb.x1,
20881 y: bb.y1
20882 };
20883 });
20884 } else {
20885 // width/height * splits^2 = cells where splits is number of times to split width
20886 var cells = nodes.size();
20887 var splits = Math.sqrt(cells * bb.h / bb.w);
20888 var rows = Math.round(splits);
20889 var cols = Math.round(bb.w / bb.h * splits);
20890
20891 var small = function small(val) {
20892 if (val == null) {
20893 return Math.min(rows, cols);
20894 } else {
20895 var min = Math.min(rows, cols);
20896
20897 if (min == rows) {
20898 rows = val;
20899 } else {
20900 cols = val;
20901 }
20902 }
20903 };
20904
20905 var large = function large(val) {
20906 if (val == null) {
20907 return Math.max(rows, cols);
20908 } else {
20909 var max = Math.max(rows, cols);
20910
20911 if (max == rows) {
20912 rows = val;
20913 } else {
20914 cols = val;
20915 }
20916 }
20917 };
20918
20919 var oRows = options.rows;
20920 var oCols = options.cols != null ? options.cols : options.columns; // if rows or columns were set in options, use those values
20921
20922 if (oRows != null && oCols != null) {
20923 rows = oRows;
20924 cols = oCols;
20925 } else if (oRows != null && oCols == null) {
20926 rows = oRows;
20927 cols = Math.ceil(cells / rows);
20928 } else if (oRows == null && oCols != null) {
20929 cols = oCols;
20930 rows = Math.ceil(cells / cols);
20931 } // otherwise use the automatic values and adjust accordingly
20932 // if rounding was up, see if we can reduce rows or columns
20933 else if (cols * rows > cells) {
20934 var sm = small();
20935 var lg = large(); // reducing the small side takes away the most cells, so try it first
20936
20937 if ((sm - 1) * lg >= cells) {
20938 small(sm - 1);
20939 } else if ((lg - 1) * sm >= cells) {
20940 large(lg - 1);
20941 }
20942 } else {
20943 // if rounding was too low, add rows or columns
20944 while (cols * rows < cells) {
20945 var _sm = small();
20946
20947 var _lg = large(); // try to add to larger side first (adds less in multiplication)
20948
20949
20950 if ((_lg + 1) * _sm >= cells) {
20951 large(_lg + 1);
20952 } else {
20953 small(_sm + 1);
20954 }
20955 }
20956 }
20957
20958 var cellWidth = bb.w / cols;
20959 var cellHeight = bb.h / rows;
20960
20961 if (options.condense) {
20962 cellWidth = 0;
20963 cellHeight = 0;
20964 }
20965
20966 if (options.avoidOverlap) {
20967 for (var i = 0; i < nodes.length; i++) {
20968 var node = nodes[i];
20969 var pos = node._private.position;
20970
20971 if (pos.x == null || pos.y == null) {
20972 // for bb
20973 pos.x = 0;
20974 pos.y = 0;
20975 }
20976
20977 var nbb = node.layoutDimensions(options);
20978 var p = options.avoidOverlapPadding;
20979 var w = nbb.w + p;
20980 var h = nbb.h + p;
20981 cellWidth = Math.max(cellWidth, w);
20982 cellHeight = Math.max(cellHeight, h);
20983 }
20984 }
20985
20986 var cellUsed = {}; // e.g. 'c-0-2' => true
20987
20988 var used = function used(row, col) {
20989 return cellUsed['c-' + row + '-' + col] ? true : false;
20990 };
20991
20992 var use = function use(row, col) {
20993 cellUsed['c-' + row + '-' + col] = true;
20994 }; // to keep track of current cell position
20995
20996
20997 var row = 0;
20998 var col = 0;
20999
21000 var moveToNextCell = function moveToNextCell() {
21001 col++;
21002
21003 if (col >= cols) {
21004 col = 0;
21005 row++;
21006 }
21007 }; // get a cache of all the manual positions
21008
21009
21010 var id2manPos = {};
21011
21012 for (var _i = 0; _i < nodes.length; _i++) {
21013 var _node = nodes[_i];
21014 var rcPos = options.position(_node);
21015
21016 if (rcPos && (rcPos.row !== undefined || rcPos.col !== undefined)) {
21017 // must have at least row or col def'd
21018 var _pos = {
21019 row: rcPos.row,
21020 col: rcPos.col
21021 };
21022
21023 if (_pos.col === undefined) {
21024 // find unused col
21025 _pos.col = 0;
21026
21027 while (used(_pos.row, _pos.col)) {
21028 _pos.col++;
21029 }
21030 } else if (_pos.row === undefined) {
21031 // find unused row
21032 _pos.row = 0;
21033
21034 while (used(_pos.row, _pos.col)) {
21035 _pos.row++;
21036 }
21037 }
21038
21039 id2manPos[_node.id()] = _pos;
21040 use(_pos.row, _pos.col);
21041 }
21042 }
21043
21044 var getPos = function getPos(element, i) {
21045 var x, y;
21046
21047 if (element.locked() || element.isParent()) {
21048 return false;
21049 } // see if we have a manual position set
21050
21051
21052 var rcPos = id2manPos[element.id()];
21053
21054 if (rcPos) {
21055 x = rcPos.col * cellWidth + cellWidth / 2 + bb.x1;
21056 y = rcPos.row * cellHeight + cellHeight / 2 + bb.y1;
21057 } else {
21058 // otherwise set automatically
21059 while (used(row, col)) {
21060 moveToNextCell();
21061 }
21062
21063 x = col * cellWidth + cellWidth / 2 + bb.x1;
21064 y = row * cellHeight + cellHeight / 2 + bb.y1;
21065 use(row, col);
21066 moveToNextCell();
21067 }
21068
21069 return {
21070 x: x,
21071 y: y
21072 };
21073 };
21074
21075 nodes.layoutPositions(this, options, getPos);
21076 }
21077
21078 return this; // chaining
21079};
21080
21081var defaults$e = {
21082 ready: function ready() {},
21083 // on layoutready
21084 stop: function stop() {} // on layoutstop
21085
21086}; // constructor
21087// options : object containing layout options
21088
21089function NullLayout(options) {
21090 this.options = extend({}, defaults$e, options);
21091} // runs the layout
21092
21093
21094NullLayout.prototype.run = function () {
21095 var options = this.options;
21096 var eles = options.eles; // elements to consider in the layout
21097
21098 var layout = this; // cy is automatically populated for us in the constructor
21099 // (disable eslint for next line as this serves as example layout code to external developers)
21100 // eslint-disable-next-line no-unused-vars
21101
21102 var cy = options.cy;
21103 layout.emit('layoutstart'); // puts all nodes at (0, 0)
21104 // n.b. most layouts would use layoutPositions(), instead of positions() and manual events
21105
21106 eles.nodes().positions(function () {
21107 return {
21108 x: 0,
21109 y: 0
21110 };
21111 }); // trigger layoutready when each node has had its position set at least once
21112
21113 layout.one('layoutready', options.ready);
21114 layout.emit('layoutready'); // trigger layoutstop when the layout stops (e.g. finishes)
21115
21116 layout.one('layoutstop', options.stop);
21117 layout.emit('layoutstop');
21118 return this; // chaining
21119}; // called on continuous layouts to stop them before they finish
21120
21121
21122NullLayout.prototype.stop = function () {
21123 return this; // chaining
21124};
21125
21126var defaults$f = {
21127 positions: undefined,
21128 // map of (node id) => (position obj); or function(node){ return somPos; }
21129 zoom: undefined,
21130 // the zoom level to set (prob want fit = false if set)
21131 pan: undefined,
21132 // the pan level to set (prob want fit = false if set)
21133 fit: true,
21134 // whether to fit to viewport
21135 padding: 30,
21136 // padding on fit
21137 animate: false,
21138 // whether to transition the node positions
21139 animationDuration: 500,
21140 // duration of animation in ms if enabled
21141 animationEasing: undefined,
21142 // easing of animation if enabled
21143 animateFilter: function animateFilter(node, i) {
21144 return true;
21145 },
21146 // 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
21147 ready: undefined,
21148 // callback on layoutready
21149 stop: undefined,
21150 // callback on layoutstop
21151 transform: function transform(node, position) {
21152 return position;
21153 } // transform a given node position. Useful for changing flow direction in discrete layouts
21154
21155};
21156
21157function PresetLayout(options) {
21158 this.options = extend({}, defaults$f, options);
21159}
21160
21161PresetLayout.prototype.run = function () {
21162 var options = this.options;
21163 var eles = options.eles;
21164 var nodes = eles.nodes();
21165 var posIsFn = fn(options.positions);
21166
21167 function getPosition(node) {
21168 if (options.positions == null) {
21169 return copyPosition(node.position());
21170 }
21171
21172 if (posIsFn) {
21173 return options.positions(node);
21174 }
21175
21176 var pos = options.positions[node._private.data.id];
21177
21178 if (pos == null) {
21179 return null;
21180 }
21181
21182 return pos;
21183 }
21184
21185 nodes.layoutPositions(this, options, function (node, i) {
21186 var position = getPosition(node);
21187
21188 if (node.locked() || position == null) {
21189 return false;
21190 }
21191
21192 return position;
21193 });
21194 return this; // chaining
21195};
21196
21197var defaults$g = {
21198 fit: true,
21199 // whether to fit to viewport
21200 padding: 30,
21201 // fit padding
21202 boundingBox: undefined,
21203 // constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
21204 animate: false,
21205 // whether to transition the node positions
21206 animationDuration: 500,
21207 // duration of animation in ms if enabled
21208 animationEasing: undefined,
21209 // easing of animation if enabled
21210 animateFilter: function animateFilter(node, i) {
21211 return true;
21212 },
21213 // 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
21214 ready: undefined,
21215 // callback on layoutready
21216 stop: undefined,
21217 // callback on layoutstop
21218 transform: function transform(node, position) {
21219 return position;
21220 } // transform a given node position. Useful for changing flow direction in discrete layouts
21221
21222};
21223
21224function RandomLayout(options) {
21225 this.options = extend({}, defaults$g, options);
21226}
21227
21228RandomLayout.prototype.run = function () {
21229 var options = this.options;
21230 var cy = options.cy;
21231 var eles = options.eles;
21232 var bb = makeBoundingBox(options.boundingBox ? options.boundingBox : {
21233 x1: 0,
21234 y1: 0,
21235 w: cy.width(),
21236 h: cy.height()
21237 });
21238
21239 var getPos = function getPos(node, i) {
21240 return {
21241 x: bb.x1 + Math.round(Math.random() * bb.w),
21242 y: bb.y1 + Math.round(Math.random() * bb.h)
21243 };
21244 };
21245
21246 eles.nodes().layoutPositions(this, options, getPos);
21247 return this; // chaining
21248};
21249
21250var layout = [{
21251 name: 'breadthfirst',
21252 impl: BreadthFirstLayout
21253}, {
21254 name: 'circle',
21255 impl: CircleLayout
21256}, {
21257 name: 'concentric',
21258 impl: ConcentricLayout
21259}, {
21260 name: 'cose',
21261 impl: CoseLayout
21262}, {
21263 name: 'grid',
21264 impl: GridLayout
21265}, {
21266 name: 'null',
21267 impl: NullLayout
21268}, {
21269 name: 'preset',
21270 impl: PresetLayout
21271}, {
21272 name: 'random',
21273 impl: RandomLayout
21274}];
21275
21276function NullRenderer(options) {
21277 this.options = options;
21278 this.notifications = 0; // for testing
21279}
21280
21281var noop$1 = function noop() {};
21282
21283var throwImgErr = function throwImgErr() {
21284 throw new Error('A headless instance can not render images');
21285};
21286
21287NullRenderer.prototype = {
21288 recalculateRenderedStyle: noop$1,
21289 notify: function notify() {
21290 this.notifications++;
21291 },
21292 init: noop$1,
21293 isHeadless: function isHeadless() {
21294 return true;
21295 },
21296 png: throwImgErr,
21297 jpg: throwImgErr
21298};
21299
21300var BRp = {};
21301BRp.arrowShapeWidth = 0.3;
21302
21303BRp.registerArrowShapes = function () {
21304 var arrowShapes = this.arrowShapes = {};
21305 var renderer = this; // Contract for arrow shapes:
21306 // 0, 0 is arrow tip
21307 // (0, 1) is direction towards node
21308 // (1, 0) is right
21309 //
21310 // functional api:
21311 // collide: check x, y in shape
21312 // roughCollide: called before collide, no false negatives
21313 // draw: draw
21314 // spacing: dist(arrowTip, nodeBoundary)
21315 // gap: dist(edgeTip, nodeBoundary), edgeTip may != arrowTip
21316
21317 var bbCollide = function bbCollide(x, y, size, angle, translation, edgeWidth, padding) {
21318 var x1 = translation.x - size / 2 - padding;
21319 var x2 = translation.x + size / 2 + padding;
21320 var y1 = translation.y - size / 2 - padding;
21321 var y2 = translation.y + size / 2 + padding;
21322 var inside = x1 <= x && x <= x2 && y1 <= y && y <= y2;
21323 return inside;
21324 };
21325
21326 var transform = function transform(x, y, size, angle, translation) {
21327 var xRotated = x * Math.cos(angle) - y * Math.sin(angle);
21328 var yRotated = x * Math.sin(angle) + y * Math.cos(angle);
21329 var xScaled = xRotated * size;
21330 var yScaled = yRotated * size;
21331 var xTranslated = xScaled + translation.x;
21332 var yTranslated = yScaled + translation.y;
21333 return {
21334 x: xTranslated,
21335 y: yTranslated
21336 };
21337 };
21338
21339 var transformPoints = function transformPoints(pts, size, angle, translation) {
21340 var retPts = [];
21341
21342 for (var i = 0; i < pts.length; i += 2) {
21343 var x = pts[i];
21344 var y = pts[i + 1];
21345 retPts.push(transform(x, y, size, angle, translation));
21346 }
21347
21348 return retPts;
21349 };
21350
21351 var pointsToArr = function pointsToArr(pts) {
21352 var ret = [];
21353
21354 for (var i = 0; i < pts.length; i++) {
21355 var p = pts[i];
21356 ret.push(p.x, p.y);
21357 }
21358
21359 return ret;
21360 };
21361
21362 var standardGap = function standardGap(edge) {
21363 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').pfValue * 2;
21364 };
21365
21366 var defineArrowShape = function defineArrowShape(name, defn) {
21367 if (string(defn)) {
21368 defn = arrowShapes[defn];
21369 }
21370
21371 arrowShapes[name] = extend({
21372 name: name,
21373 points: [-0.15, -0.3, 0.15, -0.3, 0.15, 0.3, -0.15, 0.3],
21374 collide: function collide(x, y, size, angle, translation, padding) {
21375 var points = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21376 var inside = pointInsidePolygonPoints(x, y, points);
21377 return inside;
21378 },
21379 roughCollide: bbCollide,
21380 draw: function draw(context, size, angle, translation) {
21381 var points = transformPoints(this.points, size, angle, translation);
21382 renderer.arrowShapeImpl('polygon')(context, points);
21383 },
21384 spacing: function spacing(edge) {
21385 return 0;
21386 },
21387 gap: standardGap
21388 }, defn);
21389 };
21390
21391 defineArrowShape('none', {
21392 collide: falsify,
21393 roughCollide: falsify,
21394 draw: noop,
21395 spacing: zeroify,
21396 gap: zeroify
21397 });
21398 defineArrowShape('triangle', {
21399 points: [-0.15, -0.3, 0, 0, 0.15, -0.3]
21400 });
21401 defineArrowShape('arrow', 'triangle');
21402 defineArrowShape('triangle-backcurve', {
21403 points: arrowShapes['triangle'].points,
21404 controlPoint: [0, -0.15],
21405 roughCollide: bbCollide,
21406 draw: function draw(context, size, angle, translation, edgeWidth) {
21407 var ptsTrans = transformPoints(this.points, size, angle, translation);
21408 var ctrlPt = this.controlPoint;
21409 var ctrlPtTrans = transform(ctrlPt[0], ctrlPt[1], size, angle, translation);
21410 renderer.arrowShapeImpl(this.name)(context, ptsTrans, ctrlPtTrans);
21411 },
21412 gap: function gap(edge) {
21413 return standardGap(edge) * 0.8;
21414 }
21415 });
21416 defineArrowShape('triangle-tee', {
21417 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21418 pointsTee: [-0.15, -0.4, -0.15, -0.5, 0.15, -0.5, 0.15, -0.4],
21419 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21420 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21421 var teePts = pointsToArr(transformPoints(this.pointsTee, size + 2 * padding, angle, translation));
21422 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21423 return inside;
21424 },
21425 draw: function draw(context, size, angle, translation, edgeWidth) {
21426 var triPts = transformPoints(this.points, size, angle, translation);
21427 var teePts = transformPoints(this.pointsTee, size, angle, translation);
21428 renderer.arrowShapeImpl(this.name)(context, triPts, teePts);
21429 }
21430 });
21431 defineArrowShape('circle-triangle', {
21432 radius: 0.15,
21433 pointsTr: [0, -0.15, 0.15, -0.45, -0.15, -0.45, 0, -0.15],
21434 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21435 var t = translation;
21436 var circleInside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21437 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21438 return pointInsidePolygonPoints(x, y, triPts) || circleInside;
21439 },
21440 draw: function draw(context, size, angle, translation, edgeWidth) {
21441 var triPts = transformPoints(this.pointsTr, size, angle, translation);
21442 renderer.arrowShapeImpl(this.name)(context, triPts, translation.x, translation.y, this.radius * size);
21443 },
21444 spacing: function spacing(edge) {
21445 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21446 }
21447 });
21448 defineArrowShape('triangle-cross', {
21449 points: [0, 0, 0.15, -0.3, -0.15, -0.3, 0, 0],
21450 baseCrossLinePts: [-0.15, -0.4, // first half of the rectangle
21451 -0.15, -0.4, 0.15, -0.4, // second half of the rectangle
21452 0.15, -0.4],
21453 crossLinePts: function crossLinePts(size, edgeWidth) {
21454 // shift points so that the distance between the cross points matches edge width
21455 var p = this.baseCrossLinePts.slice();
21456 var shiftFactor = edgeWidth / size;
21457 var y0 = 3;
21458 var y1 = 5;
21459 p[y0] = p[y0] - shiftFactor;
21460 p[y1] = p[y1] - shiftFactor;
21461 return p;
21462 },
21463 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21464 var triPts = pointsToArr(transformPoints(this.points, size + 2 * padding, angle, translation));
21465 var teePts = pointsToArr(transformPoints(this.crossLinePts(size, edgeWidth), size + 2 * padding, angle, translation));
21466 var inside = pointInsidePolygonPoints(x, y, triPts) || pointInsidePolygonPoints(x, y, teePts);
21467 return inside;
21468 },
21469 draw: function draw(context, size, angle, translation, edgeWidth) {
21470 var triPts = transformPoints(this.points, size, angle, translation);
21471 var crossLinePts = transformPoints(this.crossLinePts(size, edgeWidth), size, angle, translation);
21472 renderer.arrowShapeImpl(this.name)(context, triPts, crossLinePts);
21473 }
21474 });
21475 defineArrowShape('vee', {
21476 points: [-0.15, -0.3, 0, 0, 0.15, -0.3, 0, -0.15],
21477 gap: function gap(edge) {
21478 return standardGap(edge) * 0.525;
21479 }
21480 });
21481 defineArrowShape('circle', {
21482 radius: 0.15,
21483 collide: function collide(x, y, size, angle, translation, edgeWidth, padding) {
21484 var t = translation;
21485 var inside = Math.pow(t.x - x, 2) + Math.pow(t.y - y, 2) <= Math.pow((size + 2 * padding) * this.radius, 2);
21486 return inside;
21487 },
21488 draw: function draw(context, size, angle, translation, edgeWidth) {
21489 renderer.arrowShapeImpl(this.name)(context, translation.x, translation.y, this.radius * size);
21490 },
21491 spacing: function spacing(edge) {
21492 return renderer.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.radius;
21493 }
21494 });
21495 defineArrowShape('tee', {
21496 points: [-0.15, 0, -0.15, -0.1, 0.15, -0.1, 0.15, 0],
21497 spacing: function spacing(edge) {
21498 return 1;
21499 },
21500 gap: function gap(edge) {
21501 return 1;
21502 }
21503 });
21504 defineArrowShape('square', {
21505 points: [-0.15, 0.00, 0.15, 0.00, 0.15, -0.3, -0.15, -0.3]
21506 });
21507 defineArrowShape('diamond', {
21508 points: [-0.15, -0.15, 0, -0.3, 0.15, -0.15, 0, 0],
21509 gap: function gap(edge) {
21510 return edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21511 }
21512 });
21513 defineArrowShape('chevron', {
21514 points: [0, 0, -0.15, -0.15, -0.1, -0.2, 0, -0.1, 0.1, -0.2, 0.15, -0.15],
21515 gap: function gap(edge) {
21516 return 0.95 * edge.pstyle('width').pfValue * edge.pstyle('arrow-scale').value;
21517 }
21518 });
21519};
21520
21521var BRp$1 = {}; // Project mouse
21522
21523BRp$1.projectIntoViewport = function (clientX, clientY) {
21524 var cy = this.cy;
21525 var offsets = this.findContainerClientCoords();
21526 var offsetLeft = offsets[0];
21527 var offsetTop = offsets[1];
21528 var scale = offsets[4];
21529 var pan = cy.pan();
21530 var zoom = cy.zoom();
21531 var x = ((clientX - offsetLeft) / scale - pan.x) / zoom;
21532 var y = ((clientY - offsetTop) / scale - pan.y) / zoom;
21533 return [x, y];
21534};
21535
21536BRp$1.findContainerClientCoords = function () {
21537 if (this.containerBB) {
21538 return this.containerBB;
21539 }
21540
21541 var container = this.container;
21542 var rect = container.getBoundingClientRect();
21543 var style = window$1.getComputedStyle(container);
21544
21545 var styleValue = function styleValue(name) {
21546 return parseFloat(style.getPropertyValue(name));
21547 };
21548
21549 var padding = {
21550 left: styleValue('padding-left'),
21551 right: styleValue('padding-right'),
21552 top: styleValue('padding-top'),
21553 bottom: styleValue('padding-bottom')
21554 };
21555 var border = {
21556 left: styleValue('border-left-width'),
21557 right: styleValue('border-right-width'),
21558 top: styleValue('border-top-width'),
21559 bottom: styleValue('border-bottom-width')
21560 };
21561 var clientWidth = container.clientWidth;
21562 var clientHeight = container.clientHeight;
21563 var paddingHor = padding.left + padding.right;
21564 var paddingVer = padding.top + padding.bottom;
21565 var borderHor = border.left + border.right;
21566 var scale = rect.width / (clientWidth + borderHor);
21567 var unscaledW = clientWidth - paddingHor;
21568 var unscaledH = clientHeight - paddingVer;
21569 var left = rect.left + padding.left + border.left;
21570 var top = rect.top + padding.top + border.top;
21571 return this.containerBB = [left, top, unscaledW, unscaledH, scale];
21572};
21573
21574BRp$1.invalidateContainerClientCoordsCache = function () {
21575 this.containerBB = null;
21576};
21577
21578BRp$1.findNearestElement = function (x, y, interactiveElementsOnly, isTouch) {
21579 return this.findNearestElements(x, y, interactiveElementsOnly, isTouch)[0];
21580};
21581
21582BRp$1.findNearestElements = function (x, y, interactiveElementsOnly, isTouch) {
21583 var self = this;
21584 var r = this;
21585 var eles = r.getCachedZSortedEles();
21586 var near = []; // 1 node max, 1 edge max
21587
21588 var zoom = r.cy.zoom();
21589 var hasCompounds = r.cy.hasCompoundNodes();
21590 var edgeThreshold = (isTouch ? 24 : 8) / zoom;
21591 var nodeThreshold = (isTouch ? 8 : 2) / zoom;
21592 var labelThreshold = (isTouch ? 8 : 2) / zoom;
21593 var minSqDist = Infinity;
21594 var nearEdge;
21595 var nearNode;
21596
21597 if (interactiveElementsOnly) {
21598 eles = eles.interactive;
21599 }
21600
21601 function addEle(ele, sqDist) {
21602 if (ele.isNode()) {
21603 if (nearNode) {
21604 return; // can't replace node
21605 } else {
21606 nearNode = ele;
21607 near.push(ele);
21608 }
21609 }
21610
21611 if (ele.isEdge() && (sqDist == null || sqDist < minSqDist)) {
21612 if (nearEdge) {
21613 // then replace existing edge
21614 // can replace only if same z-index
21615 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) {
21616 for (var i = 0; i < near.length; i++) {
21617 if (near[i].isEdge()) {
21618 near[i] = ele;
21619 nearEdge = ele;
21620 minSqDist = sqDist != null ? sqDist : minSqDist;
21621 break;
21622 }
21623 }
21624 }
21625 } else {
21626 near.push(ele);
21627 nearEdge = ele;
21628 minSqDist = sqDist != null ? sqDist : minSqDist;
21629 }
21630 }
21631 }
21632
21633 function checkNode(node) {
21634 var width = node.outerWidth() + 2 * nodeThreshold;
21635 var height = node.outerHeight() + 2 * nodeThreshold;
21636 var hw = width / 2;
21637 var hh = height / 2;
21638 var pos = node.position();
21639
21640 if (pos.x - hw <= x && x <= pos.x + hw // bb check x
21641 && pos.y - hh <= y && y <= pos.y + hh // bb check y
21642 ) {
21643 var shape = r.nodeShapes[self.getNodeShape(node)];
21644
21645 if (shape.checkPoint(x, y, 0, width, height, pos.x, pos.y)) {
21646 addEle(node, 0);
21647 return true;
21648 }
21649 }
21650 }
21651
21652 function checkEdge(edge) {
21653 var _p = edge._private;
21654 var rs = _p.rscratch;
21655 var styleWidth = edge.pstyle('width').pfValue;
21656 var scale = edge.pstyle('arrow-scale').value;
21657 var width = styleWidth / 2 + edgeThreshold; // more like a distance radius from centre
21658
21659 var widthSq = width * width;
21660 var width2 = width * 2;
21661 var src = _p.source;
21662 var tgt = _p.target;
21663 var sqDist;
21664
21665 if (rs.edgeType === 'segments' || rs.edgeType === 'straight' || rs.edgeType === 'haystack') {
21666 var pts = rs.allpts;
21667
21668 for (var i = 0; i + 3 < pts.length; i += 2) {
21669 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]))) {
21670 addEle(edge, sqDist);
21671 return true;
21672 }
21673 }
21674 } else if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
21675 var pts = rs.allpts;
21676
21677 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
21678 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]))) {
21679 addEle(edge, sqDist);
21680 return true;
21681 }
21682 }
21683 } // if we're close to the edge but didn't hit it, maybe we hit its arrows
21684
21685
21686 var src = src || _p.source;
21687 var tgt = tgt || _p.target;
21688 var arSize = self.getArrowWidth(styleWidth, scale);
21689 var arrows = [{
21690 name: 'source',
21691 x: rs.arrowStartX,
21692 y: rs.arrowStartY,
21693 angle: rs.srcArrowAngle
21694 }, {
21695 name: 'target',
21696 x: rs.arrowEndX,
21697 y: rs.arrowEndY,
21698 angle: rs.tgtArrowAngle
21699 }, {
21700 name: 'mid-source',
21701 x: rs.midX,
21702 y: rs.midY,
21703 angle: rs.midsrcArrowAngle
21704 }, {
21705 name: 'mid-target',
21706 x: rs.midX,
21707 y: rs.midY,
21708 angle: rs.midtgtArrowAngle
21709 }];
21710
21711 for (var i = 0; i < arrows.length; i++) {
21712 var ar = arrows[i];
21713 var shape = r.arrowShapes[edge.pstyle(ar.name + '-arrow-shape').value];
21714 var edgeWidth = edge.pstyle('width').pfValue;
21715
21716 if (shape.roughCollide(x, y, arSize, ar.angle, {
21717 x: ar.x,
21718 y: ar.y
21719 }, edgeWidth, edgeThreshold) && shape.collide(x, y, arSize, ar.angle, {
21720 x: ar.x,
21721 y: ar.y
21722 }, edgeWidth, edgeThreshold)) {
21723 addEle(edge);
21724 return true;
21725 }
21726 } // for compound graphs, hitting edge may actually want a connected node instead (b/c edge may have greater z-index precedence)
21727
21728
21729 if (hasCompounds && near.length > 0) {
21730 checkNode(src);
21731 checkNode(tgt);
21732 }
21733 }
21734
21735 function preprop(obj, name, pre) {
21736 return getPrefixedProperty(obj, name, pre);
21737 }
21738
21739 function checkLabel(ele, prefix) {
21740 var _p = ele._private;
21741 var th = labelThreshold;
21742 var prefixDash;
21743
21744 if (prefix) {
21745 prefixDash = prefix + '-';
21746 } else {
21747 prefixDash = '';
21748 }
21749
21750 ele.boundingBox();
21751 var bb = _p.labelBounds[prefix || 'main'];
21752 var text = ele.pstyle(prefixDash + 'label').value;
21753 var eventsEnabled = ele.pstyle('text-events').strValue === 'yes';
21754
21755 if (!eventsEnabled || !text) {
21756 return;
21757 }
21758
21759 var lx = preprop(_p.rscratch, 'labelX', prefix);
21760 var ly = preprop(_p.rscratch, 'labelY', prefix);
21761 var theta = preprop(_p.rscratch, 'labelAngle', prefix);
21762 var ox = ele.pstyle(prefixDash + 'text-margin-x').pfValue;
21763 var oy = ele.pstyle(prefixDash + 'text-margin-y').pfValue;
21764 var lx1 = bb.x1 - th - ox; // (-ox, -oy) as bb already includes margin
21765
21766 var lx2 = bb.x2 + th - ox; // and rotation is about (lx, ly)
21767
21768 var ly1 = bb.y1 - th - oy;
21769 var ly2 = bb.y2 + th - oy;
21770
21771 if (theta) {
21772 var cos = Math.cos(theta);
21773 var sin = Math.sin(theta);
21774
21775 var rotate = function rotate(x, y) {
21776 x = x - lx;
21777 y = y - ly;
21778 return {
21779 x: x * cos - y * sin + lx,
21780 y: x * sin + y * cos + ly
21781 };
21782 };
21783
21784 var px1y1 = rotate(lx1, ly1);
21785 var px1y2 = rotate(lx1, ly2);
21786 var px2y1 = rotate(lx2, ly1);
21787 var px2y2 = rotate(lx2, ly2);
21788 var points = [// with the margin added after the rotation is applied
21789 px1y1.x + ox, px1y1.y + oy, px2y1.x + ox, px2y1.y + oy, px2y2.x + ox, px2y2.y + oy, px1y2.x + ox, px1y2.y + oy];
21790
21791 if (pointInsidePolygonPoints(x, y, points)) {
21792 addEle(ele);
21793 return true;
21794 }
21795 } else {
21796 // do a cheaper bb check
21797 if (inBoundingBox(bb, x, y)) {
21798 addEle(ele);
21799 return true;
21800 }
21801 }
21802 }
21803
21804 for (var i = eles.length - 1; i >= 0; i--) {
21805 // reverse order for precedence
21806 var ele = eles[i];
21807
21808 if (ele.isNode()) {
21809 checkNode(ele) || checkLabel(ele);
21810 } else {
21811 // then edge
21812 checkEdge(ele) || checkLabel(ele) || checkLabel(ele, 'source') || checkLabel(ele, 'target');
21813 }
21814 }
21815
21816 return near;
21817}; // 'Give me everything from this box'
21818
21819
21820BRp$1.getAllInBox = function (x1, y1, x2, y2) {
21821 var eles = this.getCachedZSortedEles().interactive;
21822 var box = [];
21823 var x1c = Math.min(x1, x2);
21824 var x2c = Math.max(x1, x2);
21825 var y1c = Math.min(y1, y2);
21826 var y2c = Math.max(y1, y2);
21827 x1 = x1c;
21828 x2 = x2c;
21829 y1 = y1c;
21830 y2 = y2c;
21831 var boxBb = makeBoundingBox({
21832 x1: x1,
21833 y1: y1,
21834 x2: x2,
21835 y2: y2
21836 });
21837
21838 for (var e = 0; e < eles.length; e++) {
21839 var ele = eles[e];
21840
21841 if (ele.isNode()) {
21842 var node = ele;
21843 var nodeBb = node.boundingBox({
21844 includeNodes: true,
21845 includeEdges: false,
21846 includeLabels: false
21847 });
21848
21849 if (boundingBoxesIntersect(boxBb, nodeBb) && !boundingBoxInBoundingBox(nodeBb, boxBb)) {
21850 box.push(node);
21851 }
21852 } else {
21853 var edge = ele;
21854 var _p = edge._private;
21855 var rs = _p.rscratch;
21856
21857 if (rs.startX != null && rs.startY != null && !inBoundingBox(boxBb, rs.startX, rs.startY)) {
21858 continue;
21859 }
21860
21861 if (rs.endX != null && rs.endY != null && !inBoundingBox(boxBb, rs.endX, rs.endY)) {
21862 continue;
21863 }
21864
21865 if (rs.edgeType === 'bezier' || rs.edgeType === 'multibezier' || rs.edgeType === 'self' || rs.edgeType === 'compound' || rs.edgeType === 'segments' || rs.edgeType === 'haystack') {
21866 var pts = _p.rstyle.bezierPts || _p.rstyle.linePts || _p.rstyle.haystackPts;
21867 var allInside = true;
21868
21869 for (var i = 0; i < pts.length; i++) {
21870 if (!pointInBoundingBox(boxBb, pts[i])) {
21871 allInside = false;
21872 break;
21873 }
21874 }
21875
21876 if (allInside) {
21877 box.push(edge);
21878 }
21879 } else if (rs.edgeType === 'haystack' || rs.edgeType === 'straight') {
21880 box.push(edge);
21881 }
21882 }
21883 }
21884
21885 return box;
21886};
21887
21888var BRp$2 = {};
21889
21890BRp$2.calculateArrowAngles = function (edge) {
21891 var rs = edge._private.rscratch;
21892 var isHaystack = rs.edgeType === 'haystack';
21893 var isBezier = rs.edgeType === 'bezier';
21894 var isMultibezier = rs.edgeType === 'multibezier';
21895 var isSegments = rs.edgeType === 'segments';
21896 var isCompound = rs.edgeType === 'compound';
21897 var isSelf = rs.edgeType === 'self'; // Displacement gives direction for arrowhead orientation
21898
21899 var dispX, dispY;
21900 var startX, startY, endX, endY, midX, midY;
21901
21902 if (isHaystack) {
21903 startX = rs.haystackPts[0];
21904 startY = rs.haystackPts[1];
21905 endX = rs.haystackPts[2];
21906 endY = rs.haystackPts[3];
21907 } else {
21908 startX = rs.arrowStartX;
21909 startY = rs.arrowStartY;
21910 endX = rs.arrowEndX;
21911 endY = rs.arrowEndY;
21912 }
21913
21914 midX = rs.midX;
21915 midY = rs.midY; // source
21916 //
21917
21918 if (isSegments) {
21919 dispX = startX - rs.segpts[0];
21920 dispY = startY - rs.segpts[1];
21921 } else if (isMultibezier || isCompound || isSelf || isBezier) {
21922 var pts = rs.allpts;
21923 var bX = qbezierAt(pts[0], pts[2], pts[4], 0.1);
21924 var bY = qbezierAt(pts[1], pts[3], pts[5], 0.1);
21925 dispX = startX - bX;
21926 dispY = startY - bY;
21927 } else {
21928 dispX = startX - midX;
21929 dispY = startY - midY;
21930 }
21931
21932 rs.srcArrowAngle = getAngleFromDisp(dispX, dispY); // mid target
21933 //
21934
21935 var midX = rs.midX;
21936 var midY = rs.midY;
21937
21938 if (isHaystack) {
21939 midX = (startX + endX) / 2;
21940 midY = (startY + endY) / 2;
21941 }
21942
21943 dispX = endX - startX;
21944 dispY = endY - startY;
21945
21946 if (isSegments) {
21947 var pts = rs.allpts;
21948
21949 if (pts.length / 2 % 2 === 0) {
21950 var i2 = pts.length / 2;
21951 var i1 = i2 - 2;
21952 dispX = pts[i2] - pts[i1];
21953 dispY = pts[i2 + 1] - pts[i1 + 1];
21954 } else {
21955 var i2 = pts.length / 2 - 1;
21956 var i1 = i2 - 2;
21957 var i3 = i2 + 2;
21958 dispX = pts[i2] - pts[i1];
21959 dispY = pts[i2 + 1] - pts[i1 + 1];
21960 }
21961 } else if (isMultibezier || isCompound || isSelf) {
21962 var pts = rs.allpts;
21963 var cpts = rs.ctrlpts;
21964 var bp0x, bp0y;
21965 var bp1x, bp1y;
21966
21967 if (cpts.length / 2 % 2 === 0) {
21968 var p0 = pts.length / 2 - 1; // startpt
21969
21970 var ic = p0 + 2;
21971 var p1 = ic + 2;
21972 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0);
21973 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0);
21974 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.0001);
21975 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.0001);
21976 } else {
21977 var ic = pts.length / 2 - 1; // ctrpt
21978
21979 var p0 = ic - 2; // startpt
21980
21981 var p1 = ic + 2; // endpt
21982
21983 bp0x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.4999);
21984 bp0y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.4999);
21985 bp1x = qbezierAt(pts[p0], pts[ic], pts[p1], 0.5);
21986 bp1y = qbezierAt(pts[p0 + 1], pts[ic + 1], pts[p1 + 1], 0.5);
21987 }
21988
21989 dispX = bp1x - bp0x;
21990 dispY = bp1y - bp0y;
21991 }
21992
21993 rs.midtgtArrowAngle = getAngleFromDisp(dispX, dispY);
21994 rs.midDispX = dispX;
21995 rs.midDispY = dispY; // mid source
21996 //
21997
21998 dispX *= -1;
21999 dispY *= -1;
22000
22001 if (isSegments) {
22002 var pts = rs.allpts;
22003
22004 if (pts.length / 2 % 2 === 0) ; else {
22005 var i2 = pts.length / 2 - 1;
22006 var i3 = i2 + 2;
22007 dispX = -(pts[i3] - pts[i2]);
22008 dispY = -(pts[i3 + 1] - pts[i2 + 1]);
22009 }
22010 }
22011
22012 rs.midsrcArrowAngle = getAngleFromDisp(dispX, dispY); // target
22013 //
22014
22015 if (isSegments) {
22016 dispX = endX - rs.segpts[rs.segpts.length - 2];
22017 dispY = endY - rs.segpts[rs.segpts.length - 1];
22018 } else if (isMultibezier || isCompound || isSelf || isBezier) {
22019 var pts = rs.allpts;
22020 var l = pts.length;
22021 var bX = qbezierAt(pts[l - 6], pts[l - 4], pts[l - 2], 0.9);
22022 var bY = qbezierAt(pts[l - 5], pts[l - 3], pts[l - 1], 0.9);
22023 dispX = endX - bX;
22024 dispY = endY - bY;
22025 } else {
22026 dispX = endX - midX;
22027 dispY = endY - midY;
22028 }
22029
22030 rs.tgtArrowAngle = getAngleFromDisp(dispX, dispY);
22031};
22032
22033BRp$2.getArrowWidth = BRp$2.getArrowHeight = function (edgeWidth, scale) {
22034 var cache = this.arrowWidthCache = this.arrowWidthCache || {};
22035 var cachedVal = cache[edgeWidth + ', ' + scale];
22036
22037 if (cachedVal) {
22038 return cachedVal;
22039 }
22040
22041 cachedVal = Math.max(Math.pow(edgeWidth * 13.37, 0.9), 29) * scale;
22042 cache[edgeWidth + ', ' + scale] = cachedVal;
22043 return cachedVal;
22044};
22045
22046var BRp$3 = {};
22047
22048BRp$3.findHaystackPoints = function (edges) {
22049 for (var i = 0; i < edges.length; i++) {
22050 var edge = edges[i];
22051 var _p = edge._private;
22052 var rs = _p.rscratch;
22053
22054 if (!rs.haystack) {
22055 var angle = Math.random() * 2 * Math.PI;
22056 rs.source = {
22057 x: Math.cos(angle),
22058 y: Math.sin(angle)
22059 };
22060 angle = Math.random() * 2 * Math.PI;
22061 rs.target = {
22062 x: Math.cos(angle),
22063 y: Math.sin(angle)
22064 };
22065 }
22066
22067 var src = _p.source;
22068 var tgt = _p.target;
22069 var srcPos = src.position();
22070 var tgtPos = tgt.position();
22071 var srcW = src.width();
22072 var tgtW = tgt.width();
22073 var srcH = src.height();
22074 var tgtH = tgt.height();
22075 var radius = edge.pstyle('haystack-radius').value;
22076 var halfRadius = radius / 2; // b/c have to half width/height
22077
22078 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];
22079 rs.midX = (rs.allpts[0] + rs.allpts[2]) / 2;
22080 rs.midY = (rs.allpts[1] + rs.allpts[3]) / 2; // always override as haystack in case set to different type previously
22081
22082 rs.edgeType = 'haystack';
22083 rs.haystack = true;
22084 this.storeEdgeProjections(edge);
22085 this.calculateArrowAngles(edge);
22086 this.recalculateEdgeLabelProjections(edge);
22087 this.calculateLabelAngles(edge);
22088 }
22089};
22090
22091BRp$3.findSegmentsPoints = function (edge, pairInfo) {
22092 // Segments (multiple straight lines)
22093 var rs = edge._private.rscratch;
22094 var posPts = pairInfo.posPts,
22095 intersectionPts = pairInfo.intersectionPts,
22096 vectorNormInverse = pairInfo.vectorNormInverse;
22097 var edgeDistances = edge.pstyle('edge-distances').value;
22098 var segmentWs = edge.pstyle('segment-weights');
22099 var segmentDs = edge.pstyle('segment-distances');
22100 var segmentsN = Math.min(segmentWs.pfValue.length, segmentDs.pfValue.length);
22101 rs.edgeType = 'segments';
22102 rs.segpts = [];
22103
22104 for (var s = 0; s < segmentsN; s++) {
22105 var w = segmentWs.pfValue[s];
22106 var d = segmentDs.pfValue[s];
22107 var w1 = 1 - w;
22108 var w2 = w;
22109 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22110 var adjustedMidpt = {
22111 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22112 y: midptPts.y1 * w1 + midptPts.y2 * w2
22113 };
22114 rs.segpts.push(adjustedMidpt.x + vectorNormInverse.x * d, adjustedMidpt.y + vectorNormInverse.y * d);
22115 }
22116};
22117
22118BRp$3.findLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22119 // Self-edge
22120 var rs = edge._private.rscratch;
22121 var dirCounts = pairInfo.dirCounts,
22122 srcPos = pairInfo.srcPos;
22123 var ctrlptDists = edge.pstyle('control-point-distances');
22124 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22125 var loopDir = edge.pstyle('loop-direction').pfValue;
22126 var loopSwp = edge.pstyle('loop-sweep').pfValue;
22127 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22128 rs.edgeType = 'self';
22129 var j = i;
22130 var loopDist = stepSize;
22131
22132 if (edgeIsUnbundled) {
22133 j = 0;
22134 loopDist = ctrlptDist;
22135 }
22136
22137 var loopAngle = loopDir - Math.PI / 2;
22138 var outAngle = loopAngle - loopSwp / 2;
22139 var inAngle = loopAngle + loopSwp / 2; // increase by step size for overlapping loops, keyed on direction and sweep values
22140
22141 var dc = String(loopDir + '_' + loopSwp);
22142 j = dirCounts[dc] === undefined ? dirCounts[dc] = 0 : ++dirCounts[dc];
22143 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)];
22144};
22145
22146BRp$3.findCompoundLoopPoints = function (edge, pairInfo, i, edgeIsUnbundled) {
22147 // Compound edge
22148 var rs = edge._private.rscratch;
22149 rs.edgeType = 'compound';
22150 var srcPos = pairInfo.srcPos,
22151 tgtPos = pairInfo.tgtPos,
22152 srcW = pairInfo.srcW,
22153 srcH = pairInfo.srcH,
22154 tgtW = pairInfo.tgtW,
22155 tgtH = pairInfo.tgtH;
22156 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22157 var ctrlptDists = edge.pstyle('control-point-distances');
22158 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22159 var j = i;
22160 var loopDist = stepSize;
22161
22162 if (edgeIsUnbundled) {
22163 j = 0;
22164 loopDist = ctrlptDist;
22165 }
22166
22167 var loopW = 50;
22168 var loopaPos = {
22169 x: srcPos.x - srcW / 2,
22170 y: srcPos.y - srcH / 2
22171 };
22172 var loopbPos = {
22173 x: tgtPos.x - tgtW / 2,
22174 y: tgtPos.y - tgtH / 2
22175 };
22176 var loopPos = {
22177 x: Math.min(loopaPos.x, loopbPos.x),
22178 y: Math.min(loopaPos.y, loopbPos.y)
22179 }; // avoids cases with impossible beziers
22180
22181 var minCompoundStretch = 0.5;
22182 var compoundStretchA = Math.max(minCompoundStretch, Math.log(srcW * 0.01));
22183 var compoundStretchB = Math.max(minCompoundStretch, Math.log(tgtW * 0.01));
22184 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];
22185};
22186
22187BRp$3.findStraightEdgePoints = function (edge) {
22188 // Straight edge within bundle
22189 edge._private.rscratch.edgeType = 'straight';
22190};
22191
22192BRp$3.findBezierPoints = function (edge, pairInfo, i, edgeIsUnbundled, edgeIsSwapped) {
22193 var rs = edge._private.rscratch;
22194 var vectorNormInverse = pairInfo.vectorNormInverse,
22195 posPts = pairInfo.posPts,
22196 intersectionPts = pairInfo.intersectionPts;
22197 var edgeDistances = edge.pstyle('edge-distances').value;
22198 var stepSize = edge.pstyle('control-point-step-size').pfValue;
22199 var ctrlptDists = edge.pstyle('control-point-distances');
22200 var ctrlptWs = edge.pstyle('control-point-weights');
22201 var bezierN = ctrlptDists && ctrlptWs ? Math.min(ctrlptDists.value.length, ctrlptWs.value.length) : 1;
22202 var ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[0] : undefined;
22203 var ctrlptWeight = ctrlptWs.value[0]; // (Multi)bezier
22204
22205 var multi = edgeIsUnbundled;
22206 rs.edgeType = multi ? 'multibezier' : 'bezier';
22207 rs.ctrlpts = [];
22208
22209 for (var b = 0; b < bezierN; b++) {
22210 var normctrlptDist = (0.5 - pairInfo.eles.length / 2 + i) * stepSize * (edgeIsSwapped ? -1 : 1);
22211 var manctrlptDist = void 0;
22212 var sign = signum(normctrlptDist);
22213
22214 if (multi) {
22215 ctrlptDist = ctrlptDists ? ctrlptDists.pfValue[b] : stepSize; // fall back on step size
22216
22217 ctrlptWeight = ctrlptWs.value[b];
22218 }
22219
22220 if (edgeIsUnbundled) {
22221 // multi or single unbundled
22222 manctrlptDist = ctrlptDist;
22223 } else {
22224 manctrlptDist = ctrlptDist !== undefined ? sign * ctrlptDist : undefined;
22225 }
22226
22227 var distanceFromMidpoint = manctrlptDist !== undefined ? manctrlptDist : normctrlptDist;
22228 var w1 = 1 - ctrlptWeight;
22229 var w2 = ctrlptWeight;
22230 var midptPts = edgeDistances === 'node-position' ? posPts : intersectionPts;
22231 var adjustedMidpt = {
22232 x: midptPts.x1 * w1 + midptPts.x2 * w2,
22233 y: midptPts.y1 * w1 + midptPts.y2 * w2
22234 };
22235 rs.ctrlpts.push(adjustedMidpt.x + vectorNormInverse.x * distanceFromMidpoint, adjustedMidpt.y + vectorNormInverse.y * distanceFromMidpoint);
22236 }
22237};
22238
22239BRp$3.findTaxiPoints = function (edge, pairInfo) {
22240 // Taxicab geometry with two turns maximum
22241 var rs = edge._private.rscratch;
22242 rs.edgeType = 'segments';
22243 var VERTICAL = 'vertical';
22244 var HORIZONTAL = 'horizontal';
22245 var LEFTWARD = 'leftward';
22246 var RIGHTWARD = 'rightward';
22247 var DOWNWARD = 'downward';
22248 var UPWARD = 'upward';
22249 var AUTO = 'auto';
22250 var posPts = pairInfo.posPts,
22251 srcW = pairInfo.srcW,
22252 srcH = pairInfo.srcH,
22253 tgtW = pairInfo.tgtW,
22254 tgtH = pairInfo.tgtH;
22255 var edgeDistances = edge.pstyle('edge-distances').value;
22256 var dIncludesNodeBody = edgeDistances !== 'node-position';
22257 var taxiDir = edge.pstyle('taxi-direction').value;
22258 var rawTaxiDir = taxiDir; // unprocessed value
22259
22260 var taxiTurn = edge.pstyle('taxi-turn');
22261 var turnIsPercent = taxiTurn.units === '%';
22262 var taxiTurnPfVal = taxiTurn.pfValue;
22263 var turnIsNegative = taxiTurnPfVal < 0; // i.e. from target side
22264
22265 var minD = edge.pstyle('taxi-turn-min-distance').pfValue;
22266 var dw = dIncludesNodeBody ? (srcW + tgtW) / 2 : 0;
22267 var dh = dIncludesNodeBody ? (srcH + tgtH) / 2 : 0;
22268 var pdx = posPts.x2 - posPts.x1;
22269 var pdy = posPts.y2 - posPts.y1; // take away the effective w/h from the magnitude of the delta value
22270
22271 var subDWH = function subDWH(dxy, dwh) {
22272 if (dxy > 0) {
22273 return Math.max(dxy - dwh, 0);
22274 } else {
22275 return Math.min(dxy + dwh, 0);
22276 }
22277 };
22278
22279 var dx = subDWH(pdx, dw);
22280 var dy = subDWH(pdy, dh);
22281 var isExplicitDir = false;
22282
22283 if (rawTaxiDir === AUTO) {
22284 taxiDir = Math.abs(dx) > Math.abs(dy) ? HORIZONTAL : VERTICAL;
22285 } else if (rawTaxiDir === UPWARD || rawTaxiDir === DOWNWARD) {
22286 taxiDir = VERTICAL;
22287 isExplicitDir = true;
22288 } else if (rawTaxiDir === LEFTWARD || rawTaxiDir === RIGHTWARD) {
22289 taxiDir = HORIZONTAL;
22290 isExplicitDir = true;
22291 }
22292
22293 var isVert = taxiDir === VERTICAL;
22294 var l = isVert ? dy : dx;
22295 var pl = isVert ? pdy : pdx;
22296 var sgnL = signum(pl);
22297 var forcedDir = false;
22298
22299 if (!(isExplicitDir && (turnIsPercent || turnIsNegative)) // forcing in this case would cause weird growing in the opposite direction
22300 && (rawTaxiDir === DOWNWARD && pl < 0 || rawTaxiDir === UPWARD && pl > 0 || rawTaxiDir === LEFTWARD && pl > 0 || rawTaxiDir === RIGHTWARD && pl < 0)) {
22301 sgnL *= -1;
22302 l = sgnL * Math.abs(l);
22303 forcedDir = true;
22304 }
22305
22306 var d;
22307
22308 if (turnIsPercent) {
22309 var p = taxiTurnPfVal < 0 ? 1 + taxiTurnPfVal : taxiTurnPfVal;
22310 d = p * l;
22311 } else {
22312 var k = taxiTurnPfVal < 0 ? l : 0;
22313 d = k + taxiTurnPfVal * sgnL;
22314 }
22315
22316 var getIsTooClose = function getIsTooClose(d) {
22317 return Math.abs(d) < minD || Math.abs(d) >= Math.abs(l);
22318 };
22319
22320 var isTooCloseSrc = getIsTooClose(d);
22321 var isTooCloseTgt = getIsTooClose(Math.abs(l) - Math.abs(d));
22322 var isTooClose = isTooCloseSrc || isTooCloseTgt;
22323
22324 if (isTooClose && !forcedDir) {
22325 // non-ideal routing
22326 if (isVert) {
22327 // vertical fallbacks
22328 var lShapeInsideSrc = Math.abs(pl) <= srcH / 2;
22329 var lShapeInsideTgt = Math.abs(pdx) <= tgtW / 2;
22330
22331 if (lShapeInsideSrc) {
22332 // horizontal Z-shape (direction not respected)
22333 var x = (posPts.x1 + posPts.x2) / 2;
22334 var y1 = posPts.y1,
22335 y2 = posPts.y2;
22336 rs.segpts = [x, y1, x, y2];
22337 } else if (lShapeInsideTgt) {
22338 // vertical Z-shape (distance not respected)
22339 var y = (posPts.y1 + posPts.y2) / 2;
22340 var x1 = posPts.x1,
22341 x2 = posPts.x2;
22342 rs.segpts = [x1, y, x2, y];
22343 } else {
22344 // L-shape fallback (turn distance not respected, but works well with tree siblings)
22345 rs.segpts = [posPts.x1, posPts.y2];
22346 }
22347 } else {
22348 // horizontal fallbacks
22349 var _lShapeInsideSrc = Math.abs(pl) <= srcW / 2;
22350
22351 var _lShapeInsideTgt = Math.abs(pdy) <= tgtH / 2;
22352
22353 if (_lShapeInsideSrc) {
22354 // vertical Z-shape (direction not respected)
22355 var _y = (posPts.y1 + posPts.y2) / 2;
22356
22357 var _x = posPts.x1,
22358 _x2 = posPts.x2;
22359 rs.segpts = [_x, _y, _x2, _y];
22360 } else if (_lShapeInsideTgt) {
22361 // horizontal Z-shape (turn distance not respected)
22362 var _x3 = (posPts.x1 + posPts.x2) / 2;
22363
22364 var _y2 = posPts.y1,
22365 _y3 = posPts.y2;
22366 rs.segpts = [_x3, _y2, _x3, _y3];
22367 } else {
22368 // L-shape (turn distance not respected, but works well for tree siblings)
22369 rs.segpts = [posPts.x2, posPts.y1];
22370 }
22371 }
22372 } else {
22373 // ideal routing
22374 if (isVert) {
22375 var _y4 = posPts.y1 + d + (dIncludesNodeBody ? srcH / 2 * sgnL : 0);
22376
22377 var _x4 = posPts.x1,
22378 _x5 = posPts.x2;
22379 rs.segpts = [_x4, _y4, _x5, _y4];
22380 } else {
22381 // horizontal
22382 var _x6 = posPts.x1 + d + (dIncludesNodeBody ? srcW / 2 * sgnL : 0);
22383
22384 var _y5 = posPts.y1,
22385 _y6 = posPts.y2;
22386 rs.segpts = [_x6, _y5, _x6, _y6];
22387 }
22388 }
22389};
22390
22391BRp$3.tryToCorrectInvalidPoints = function (edge, pairInfo) {
22392 var rs = edge._private.rscratch; // can only correct beziers for now...
22393
22394 if (rs.edgeType === 'bezier') {
22395 var srcPos = pairInfo.srcPos,
22396 tgtPos = pairInfo.tgtPos,
22397 srcW = pairInfo.srcW,
22398 srcH = pairInfo.srcH,
22399 tgtW = pairInfo.tgtW,
22400 tgtH = pairInfo.tgtH,
22401 srcShape = pairInfo.srcShape,
22402 tgtShape = pairInfo.tgtShape;
22403 var badStart = !number(rs.startX) || !number(rs.startY);
22404 var badAStart = !number(rs.arrowStartX) || !number(rs.arrowStartY);
22405 var badEnd = !number(rs.endX) || !number(rs.endY);
22406 var badAEnd = !number(rs.arrowEndX) || !number(rs.arrowEndY);
22407 var minCpADistFactor = 3;
22408 var arrowW = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
22409 var minCpADist = minCpADistFactor * arrowW;
22410 var startACpDist = dist({
22411 x: rs.ctrlpts[0],
22412 y: rs.ctrlpts[1]
22413 }, {
22414 x: rs.startX,
22415 y: rs.startY
22416 });
22417 var closeStartACp = startACpDist < minCpADist;
22418 var endACpDist = dist({
22419 x: rs.ctrlpts[0],
22420 y: rs.ctrlpts[1]
22421 }, {
22422 x: rs.endX,
22423 y: rs.endY
22424 });
22425 var closeEndACp = endACpDist < minCpADist;
22426 var overlapping = false;
22427
22428 if (badStart || badAStart || closeStartACp) {
22429 overlapping = true; // project control point along line from src centre to outside the src shape
22430 // (otherwise intersection will yield nothing)
22431
22432 var cpD = {
22433 // delta
22434 x: rs.ctrlpts[0] - srcPos.x,
22435 y: rs.ctrlpts[1] - srcPos.y
22436 };
22437 var cpL = Math.sqrt(cpD.x * cpD.x + cpD.y * cpD.y); // length of line
22438
22439 var cpM = {
22440 // normalised delta
22441 x: cpD.x / cpL,
22442 y: cpD.y / cpL
22443 };
22444 var radius = Math.max(srcW, srcH);
22445 var cpProj = {
22446 // *2 radius guarantees outside shape
22447 x: rs.ctrlpts[0] + cpM.x * 2 * radius,
22448 y: rs.ctrlpts[1] + cpM.y * 2 * radius
22449 };
22450 var srcCtrlPtIntn = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, cpProj.x, cpProj.y, 0);
22451
22452 if (closeStartACp) {
22453 rs.ctrlpts[0] = rs.ctrlpts[0] + cpM.x * (minCpADist - startACpDist);
22454 rs.ctrlpts[1] = rs.ctrlpts[1] + cpM.y * (minCpADist - startACpDist);
22455 } else {
22456 rs.ctrlpts[0] = srcCtrlPtIntn[0] + cpM.x * minCpADist;
22457 rs.ctrlpts[1] = srcCtrlPtIntn[1] + cpM.y * minCpADist;
22458 }
22459 }
22460
22461 if (badEnd || badAEnd || closeEndACp) {
22462 overlapping = true; // project control point along line from tgt centre to outside the tgt shape
22463 // (otherwise intersection will yield nothing)
22464
22465 var _cpD = {
22466 // delta
22467 x: rs.ctrlpts[0] - tgtPos.x,
22468 y: rs.ctrlpts[1] - tgtPos.y
22469 };
22470
22471 var _cpL = Math.sqrt(_cpD.x * _cpD.x + _cpD.y * _cpD.y); // length of line
22472
22473
22474 var _cpM = {
22475 // normalised delta
22476 x: _cpD.x / _cpL,
22477 y: _cpD.y / _cpL
22478 };
22479
22480 var _radius = Math.max(srcW, srcH);
22481
22482 var _cpProj = {
22483 // *2 radius guarantees outside shape
22484 x: rs.ctrlpts[0] + _cpM.x * 2 * _radius,
22485 y: rs.ctrlpts[1] + _cpM.y * 2 * _radius
22486 };
22487 var tgtCtrlPtIntn = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, _cpProj.x, _cpProj.y, 0);
22488
22489 if (closeEndACp) {
22490 rs.ctrlpts[0] = rs.ctrlpts[0] + _cpM.x * (minCpADist - endACpDist);
22491 rs.ctrlpts[1] = rs.ctrlpts[1] + _cpM.y * (minCpADist - endACpDist);
22492 } else {
22493 rs.ctrlpts[0] = tgtCtrlPtIntn[0] + _cpM.x * minCpADist;
22494 rs.ctrlpts[1] = tgtCtrlPtIntn[1] + _cpM.y * minCpADist;
22495 }
22496 }
22497
22498 if (overlapping) {
22499 // recalc endpts
22500 this.findEndpoints(edge);
22501 }
22502 }
22503};
22504
22505BRp$3.storeAllpts = function (edge) {
22506 var rs = edge._private.rscratch;
22507
22508 if (rs.edgeType === 'multibezier' || rs.edgeType === 'bezier' || rs.edgeType === 'self' || rs.edgeType === 'compound') {
22509 rs.allpts = [];
22510 rs.allpts.push(rs.startX, rs.startY);
22511
22512 for (var b = 0; b + 1 < rs.ctrlpts.length; b += 2) {
22513 // ctrl pt itself
22514 rs.allpts.push(rs.ctrlpts[b], rs.ctrlpts[b + 1]); // the midpt between ctrlpts as intermediate destination pts
22515
22516 if (b + 3 < rs.ctrlpts.length) {
22517 rs.allpts.push((rs.ctrlpts[b] + rs.ctrlpts[b + 2]) / 2, (rs.ctrlpts[b + 1] + rs.ctrlpts[b + 3]) / 2);
22518 }
22519 }
22520
22521 rs.allpts.push(rs.endX, rs.endY);
22522 var m, mt;
22523
22524 if (rs.ctrlpts.length / 2 % 2 === 0) {
22525 m = rs.allpts.length / 2 - 1;
22526 rs.midX = rs.allpts[m];
22527 rs.midY = rs.allpts[m + 1];
22528 } else {
22529 m = rs.allpts.length / 2 - 3;
22530 mt = 0.5;
22531 rs.midX = qbezierAt(rs.allpts[m], rs.allpts[m + 2], rs.allpts[m + 4], mt);
22532 rs.midY = qbezierAt(rs.allpts[m + 1], rs.allpts[m + 3], rs.allpts[m + 5], mt);
22533 }
22534 } else if (rs.edgeType === 'straight') {
22535 // need to calc these after endpts
22536 rs.allpts = [rs.startX, rs.startY, rs.endX, rs.endY]; // default midpt for labels etc
22537
22538 rs.midX = (rs.startX + rs.endX + rs.arrowStartX + rs.arrowEndX) / 4;
22539 rs.midY = (rs.startY + rs.endY + rs.arrowStartY + rs.arrowEndY) / 4;
22540 } else if (rs.edgeType === 'segments') {
22541 rs.allpts = [];
22542 rs.allpts.push(rs.startX, rs.startY);
22543 rs.allpts.push.apply(rs.allpts, rs.segpts);
22544 rs.allpts.push(rs.endX, rs.endY);
22545
22546 if (rs.segpts.length % 4 === 0) {
22547 var i2 = rs.segpts.length / 2;
22548 var i1 = i2 - 2;
22549 rs.midX = (rs.segpts[i1] + rs.segpts[i2]) / 2;
22550 rs.midY = (rs.segpts[i1 + 1] + rs.segpts[i2 + 1]) / 2;
22551 } else {
22552 var _i = rs.segpts.length / 2 - 1;
22553
22554 rs.midX = rs.segpts[_i];
22555 rs.midY = rs.segpts[_i + 1];
22556 }
22557 }
22558};
22559
22560BRp$3.checkForInvalidEdgeWarning = function (edge) {
22561 var rs = edge[0]._private.rscratch;
22562
22563 if (rs.nodesOverlap || number(rs.startX) && number(rs.startY) && number(rs.endX) && number(rs.endY)) {
22564 rs.loggedErr = false;
22565 } else {
22566 if (!rs.loggedErr) {
22567 rs.loggedErr = true;
22568 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.');
22569 }
22570 }
22571};
22572
22573BRp$3.findEdgeControlPoints = function (edges) {
22574 var _this = this;
22575
22576 if (!edges || edges.length === 0) {
22577 return;
22578 }
22579
22580 var r = this;
22581 var cy = r.cy;
22582 var hasCompounds = cy.hasCompoundNodes();
22583 var hashTable = {
22584 map: new Map$1(),
22585 get: function get(pairId) {
22586 var map2 = this.map.get(pairId[0]);
22587
22588 if (map2 != null) {
22589 return map2.get(pairId[1]);
22590 } else {
22591 return null;
22592 }
22593 },
22594 set: function set(pairId, val) {
22595 var map2 = this.map.get(pairId[0]);
22596
22597 if (map2 == null) {
22598 map2 = new Map$1();
22599 this.map.set(pairId[0], map2);
22600 }
22601
22602 map2.set(pairId[1], val);
22603 }
22604 };
22605 var pairIds = [];
22606 var haystackEdges = []; // create a table of edge (src, tgt) => list of edges between them
22607
22608 for (var i = 0; i < edges.length; i++) {
22609 var edge = edges[i];
22610 var _p = edge._private;
22611 var curveStyle = edge.pstyle('curve-style').value; // ignore edges who are not to be displayed
22612 // they shouldn't take up space
22613
22614 if (edge.removed() || !edge.takesUpSpace()) {
22615 continue;
22616 }
22617
22618 if (curveStyle === 'haystack') {
22619 haystackEdges.push(edge);
22620 continue;
22621 }
22622
22623 var edgeIsUnbundled = curveStyle === 'unbundled-bezier' || curveStyle === 'segments' || curveStyle === 'straight' || curveStyle === 'taxi';
22624 var edgeIsBezier = curveStyle === 'unbundled-bezier' || curveStyle === 'bezier';
22625 var src = _p.source;
22626 var tgt = _p.target;
22627 var srcIndex = src.poolIndex();
22628 var tgtIndex = tgt.poolIndex();
22629 var pairId = [srcIndex, tgtIndex].sort();
22630 var tableEntry = hashTable.get(pairId);
22631
22632 if (tableEntry == null) {
22633 tableEntry = {
22634 eles: []
22635 };
22636 hashTable.set(pairId, tableEntry);
22637 pairIds.push(pairId);
22638 }
22639
22640 tableEntry.eles.push(edge);
22641
22642 if (edgeIsUnbundled) {
22643 tableEntry.hasUnbundled = true;
22644 }
22645
22646 if (edgeIsBezier) {
22647 tableEntry.hasBezier = true;
22648 }
22649 } // for each pair (src, tgt), create the ctrl pts
22650 // Nested for loop is OK; total number of iterations for both loops = edgeCount
22651
22652
22653 var _loop = function _loop(p) {
22654 var pairId = pairIds[p];
22655 var pairInfo = hashTable.get(pairId);
22656 var swappedpairInfo = void 0;
22657
22658 if (!pairInfo.hasUnbundled) {
22659 var pllEdges = pairInfo.eles[0].parallelEdges().filter(function (e) {
22660 return e.isBundledBezier();
22661 });
22662 clearArray(pairInfo.eles);
22663 pllEdges.forEach(function (edge) {
22664 return pairInfo.eles.push(edge);
22665 }); // for each pair id, the edges should be sorted by index
22666
22667 pairInfo.eles.sort(function (edge1, edge2) {
22668 return edge1.poolIndex() - edge2.poolIndex();
22669 });
22670 }
22671
22672 var firstEdge = pairInfo.eles[0];
22673 var src = firstEdge.source();
22674 var tgt = firstEdge.target(); // make sure src/tgt distinction is consistent w.r.t. pairId
22675
22676 if (src.poolIndex() > tgt.poolIndex()) {
22677 var temp = src;
22678 src = tgt;
22679 tgt = temp;
22680 }
22681
22682 var srcPos = pairInfo.srcPos = src.position();
22683 var tgtPos = pairInfo.tgtPos = tgt.position();
22684 var srcW = pairInfo.srcW = src.outerWidth();
22685 var srcH = pairInfo.srcH = src.outerHeight();
22686 var tgtW = pairInfo.tgtW = tgt.outerWidth();
22687 var tgtH = pairInfo.tgtH = tgt.outerHeight();
22688
22689 var srcShape = pairInfo.srcShape = r.nodeShapes[_this.getNodeShape(src)];
22690
22691 var tgtShape = pairInfo.tgtShape = r.nodeShapes[_this.getNodeShape(tgt)];
22692
22693 pairInfo.dirCounts = {
22694 'north': 0,
22695 'west': 0,
22696 'south': 0,
22697 'east': 0,
22698 'northwest': 0,
22699 'southwest': 0,
22700 'northeast': 0,
22701 'southeast': 0
22702 };
22703
22704 for (var _i2 = 0; _i2 < pairInfo.eles.length; _i2++) {
22705 var _edge = pairInfo.eles[_i2];
22706 var rs = _edge[0]._private.rscratch;
22707
22708 var _curveStyle = _edge.pstyle('curve-style').value;
22709
22710 var _edgeIsUnbundled = _curveStyle === 'unbundled-bezier' || _curveStyle === 'segments' || _curveStyle === 'taxi'; // whether the normalised pair order is the reverse of the edge's src-tgt order
22711
22712
22713 var edgeIsSwapped = !src.same(_edge.source());
22714
22715 if (!pairInfo.calculatedIntersection && src !== tgt && (pairInfo.hasBezier || pairInfo.hasUnbundled)) {
22716 pairInfo.calculatedIntersection = true; // pt outside src shape to calc distance/displacement from src to tgt
22717
22718 var srcOutside = srcShape.intersectLine(srcPos.x, srcPos.y, srcW, srcH, tgtPos.x, tgtPos.y, 0);
22719 var srcIntn = pairInfo.srcIntn = srcOutside; // pt outside tgt shape to calc distance/displacement from src to tgt
22720
22721 var tgtOutside = tgtShape.intersectLine(tgtPos.x, tgtPos.y, tgtW, tgtH, srcPos.x, srcPos.y, 0);
22722 var tgtIntn = pairInfo.tgtIntn = tgtOutside;
22723 var intersectionPts = pairInfo.intersectionPts = {
22724 x1: srcOutside[0],
22725 x2: tgtOutside[0],
22726 y1: srcOutside[1],
22727 y2: tgtOutside[1]
22728 };
22729 var posPts = pairInfo.posPts = {
22730 x1: srcPos.x,
22731 x2: tgtPos.x,
22732 y1: srcPos.y,
22733 y2: tgtPos.y
22734 };
22735 var dy = tgtOutside[1] - srcOutside[1];
22736 var dx = tgtOutside[0] - srcOutside[0];
22737 var l = Math.sqrt(dx * dx + dy * dy);
22738 var vector = pairInfo.vector = {
22739 x: dx,
22740 y: dy
22741 };
22742 var vectorNorm = pairInfo.vectorNorm = {
22743 x: vector.x / l,
22744 y: vector.y / l
22745 };
22746 var vectorNormInverse = {
22747 x: -vectorNorm.y,
22748 y: vectorNorm.x
22749 }; // if node shapes overlap, then no ctrl pts to draw
22750
22751 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);
22752 pairInfo.vectorNormInverse = vectorNormInverse;
22753 swappedpairInfo = {
22754 nodesOverlap: pairInfo.nodesOverlap,
22755 dirCounts: pairInfo.dirCounts,
22756 calculatedIntersection: true,
22757 hasBezier: pairInfo.hasBezier,
22758 hasUnbundled: pairInfo.hasUnbundled,
22759 eles: pairInfo.eles,
22760 srcPos: tgtPos,
22761 tgtPos: srcPos,
22762 srcW: tgtW,
22763 srcH: tgtH,
22764 tgtW: srcW,
22765 tgtH: srcH,
22766 srcIntn: tgtIntn,
22767 tgtIntn: srcIntn,
22768 srcShape: tgtShape,
22769 tgtShape: srcShape,
22770 posPts: {
22771 x1: posPts.x2,
22772 y1: posPts.y2,
22773 x2: posPts.x1,
22774 y2: posPts.y1
22775 },
22776 intersectionPts: {
22777 x1: intersectionPts.x2,
22778 y1: intersectionPts.y2,
22779 x2: intersectionPts.x1,
22780 y2: intersectionPts.y1
22781 },
22782 vector: {
22783 x: -vector.x,
22784 y: -vector.y
22785 },
22786 vectorNorm: {
22787 x: -vectorNorm.x,
22788 y: -vectorNorm.y
22789 },
22790 vectorNormInverse: {
22791 x: -vectorNormInverse.x,
22792 y: -vectorNormInverse.y
22793 }
22794 };
22795 }
22796
22797 var passedPairInfo = edgeIsSwapped ? swappedpairInfo : pairInfo;
22798 rs.nodesOverlap = passedPairInfo.nodesOverlap;
22799 rs.srcIntn = passedPairInfo.srcIntn;
22800 rs.tgtIntn = passedPairInfo.tgtIntn;
22801
22802 if (hasCompounds && (src.isParent() || src.isChild() || tgt.isParent() || tgt.isChild()) && (src.parents().anySame(tgt) || tgt.parents().anySame(src) || src.same(tgt) && src.isParent())) {
22803 _this.findCompoundLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22804 } else if (src === tgt) {
22805 _this.findLoopPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled);
22806 } else if (_curveStyle === 'segments') {
22807 _this.findSegmentsPoints(_edge, passedPairInfo);
22808 } else if (_curveStyle === 'taxi') {
22809 _this.findTaxiPoints(_edge, passedPairInfo);
22810 } else if (_curveStyle === 'straight' || !_edgeIsUnbundled && pairInfo.eles.length % 2 === 1 && _i2 === Math.floor(pairInfo.eles.length / 2)) {
22811 _this.findStraightEdgePoints(_edge);
22812 } else {
22813 _this.findBezierPoints(_edge, passedPairInfo, _i2, _edgeIsUnbundled, edgeIsSwapped);
22814 }
22815
22816 _this.findEndpoints(_edge);
22817
22818 _this.tryToCorrectInvalidPoints(_edge, passedPairInfo);
22819
22820 _this.checkForInvalidEdgeWarning(_edge);
22821
22822 _this.storeAllpts(_edge);
22823
22824 _this.storeEdgeProjections(_edge);
22825
22826 _this.calculateArrowAngles(_edge);
22827
22828 _this.recalculateEdgeLabelProjections(_edge);
22829
22830 _this.calculateLabelAngles(_edge);
22831 } // for pair edges
22832
22833 };
22834
22835 for (var p = 0; p < pairIds.length; p++) {
22836 _loop(p);
22837 } // for pair ids
22838 // haystacks avoid the expense of pairInfo stuff (intersections etc.)
22839
22840
22841 this.findHaystackPoints(haystackEdges);
22842};
22843
22844function getPts(pts) {
22845 var retPts = [];
22846
22847 if (pts == null) {
22848 return;
22849 }
22850
22851 for (var i = 0; i < pts.length; i += 2) {
22852 var x = pts[i];
22853 var y = pts[i + 1];
22854 retPts.push({
22855 x: x,
22856 y: y
22857 });
22858 }
22859
22860 return retPts;
22861}
22862
22863BRp$3.getSegmentPoints = function (edge) {
22864 var rs = edge[0]._private.rscratch;
22865 var type = rs.edgeType;
22866
22867 if (type === 'segments') {
22868 this.recalculateRenderedStyle(edge);
22869 return getPts(rs.segpts);
22870 }
22871};
22872
22873BRp$3.getControlPoints = function (edge) {
22874 var rs = edge[0]._private.rscratch;
22875 var type = rs.edgeType;
22876
22877 if (type === 'bezier' || type === 'multibezier' || type === 'self' || type === 'compound') {
22878 this.recalculateRenderedStyle(edge);
22879 return getPts(rs.ctrlpts);
22880 }
22881};
22882
22883BRp$3.getEdgeMidpoint = function (edge) {
22884 var rs = edge[0]._private.rscratch;
22885 this.recalculateRenderedStyle(edge);
22886 return {
22887 x: rs.midX,
22888 y: rs.midY
22889 };
22890};
22891
22892var BRp$4 = {};
22893
22894BRp$4.manualEndptToPx = function (node, prop) {
22895 var r = this;
22896 var npos = node.position();
22897 var w = node.outerWidth();
22898 var h = node.outerHeight();
22899
22900 if (prop.value.length === 2) {
22901 var p = [prop.pfValue[0], prop.pfValue[1]];
22902
22903 if (prop.units[0] === '%') {
22904 p[0] = p[0] * w;
22905 }
22906
22907 if (prop.units[1] === '%') {
22908 p[1] = p[1] * h;
22909 }
22910
22911 p[0] += npos.x;
22912 p[1] += npos.y;
22913 return p;
22914 } else {
22915 var angle = prop.pfValue[0];
22916 angle = -Math.PI / 2 + angle; // start at 12 o'clock
22917
22918 var l = 2 * Math.max(w, h);
22919 var _p = [npos.x + Math.cos(angle) * l, npos.y + Math.sin(angle) * l];
22920 return r.nodeShapes[this.getNodeShape(node)].intersectLine(npos.x, npos.y, w, h, _p[0], _p[1], 0);
22921 }
22922};
22923
22924BRp$4.findEndpoints = function (edge) {
22925 var r = this;
22926 var intersect;
22927 var source = edge.source()[0];
22928 var target = edge.target()[0];
22929 var srcPos = source.position();
22930 var tgtPos = target.position();
22931 var tgtArShape = edge.pstyle('target-arrow-shape').value;
22932 var srcArShape = edge.pstyle('source-arrow-shape').value;
22933 var tgtDist = edge.pstyle('target-distance-from-node').pfValue;
22934 var srcDist = edge.pstyle('source-distance-from-node').pfValue;
22935 var curveStyle = edge.pstyle('curve-style').value;
22936 var rs = edge._private.rscratch;
22937 var et = rs.edgeType;
22938 var taxi = curveStyle === 'taxi';
22939 var self = et === 'self' || et === 'compound';
22940 var bezier = et === 'bezier' || et === 'multibezier' || self;
22941 var multi = et !== 'bezier';
22942 var lines = et === 'straight' || et === 'segments';
22943 var segments = et === 'segments';
22944 var hasEndpts = bezier || multi || lines;
22945 var overrideEndpts = self || taxi;
22946 var srcManEndpt = edge.pstyle('source-endpoint');
22947 var srcManEndptVal = overrideEndpts ? 'outside-to-node' : srcManEndpt.value;
22948 var tgtManEndpt = edge.pstyle('target-endpoint');
22949 var tgtManEndptVal = overrideEndpts ? 'outside-to-node' : tgtManEndpt.value;
22950 rs.srcManEndpt = srcManEndpt;
22951 rs.tgtManEndpt = tgtManEndpt;
22952 var p1; // last known point of edge on target side
22953
22954 var p2; // last known point of edge on source side
22955
22956 var p1_i; // point to intersect with target shape
22957
22958 var p2_i; // point to intersect with source shape
22959
22960 if (bezier) {
22961 var cpStart = [rs.ctrlpts[0], rs.ctrlpts[1]];
22962 var cpEnd = multi ? [rs.ctrlpts[rs.ctrlpts.length - 2], rs.ctrlpts[rs.ctrlpts.length - 1]] : cpStart;
22963 p1 = cpEnd;
22964 p2 = cpStart;
22965 } else if (lines) {
22966 var srcArrowFromPt = !segments ? [tgtPos.x, tgtPos.y] : rs.segpts.slice(0, 2);
22967 var tgtArrowFromPt = !segments ? [srcPos.x, srcPos.y] : rs.segpts.slice(rs.segpts.length - 2);
22968 p1 = tgtArrowFromPt;
22969 p2 = srcArrowFromPt;
22970 }
22971
22972 if (tgtManEndptVal === 'inside-to-node') {
22973 intersect = [tgtPos.x, tgtPos.y];
22974 } else if (tgtManEndpt.units) {
22975 intersect = this.manualEndptToPx(target, tgtManEndpt);
22976 } else if (tgtManEndptVal === 'outside-to-line') {
22977 intersect = rs.tgtIntn; // use cached value from ctrlpt calc
22978 } else {
22979 if (tgtManEndptVal === 'outside-to-node' || tgtManEndptVal === 'outside-to-node-or-label') {
22980 p1_i = p1;
22981 } else if (tgtManEndptVal === 'outside-to-line' || tgtManEndptVal === 'outside-to-line-or-label') {
22982 p1_i = [srcPos.x, srcPos.y];
22983 }
22984
22985 intersect = r.nodeShapes[this.getNodeShape(target)].intersectLine(tgtPos.x, tgtPos.y, target.outerWidth(), target.outerHeight(), p1_i[0], p1_i[1], 0);
22986
22987 if (tgtManEndptVal === 'outside-to-node-or-label' || tgtManEndptVal === 'outside-to-line-or-label') {
22988 var trs = target._private.rscratch;
22989 var lw = trs.labelWidth;
22990 var lh = trs.labelHeight;
22991 var lx = trs.labelX;
22992 var ly = trs.labelY;
22993 var lw2 = lw / 2;
22994 var lh2 = lh / 2;
22995 var va = target.pstyle('text-valign').value;
22996
22997 if (va === 'top') {
22998 ly -= lh2;
22999 } else if (va === 'bottom') {
23000 ly += lh2;
23001 }
23002
23003 var ha = target.pstyle('text-halign').value;
23004
23005 if (ha === 'left') {
23006 lx -= lw2;
23007 } else if (ha === 'right') {
23008 lx += lw2;
23009 }
23010
23011 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);
23012
23013 if (labelIntersect.length > 0) {
23014 var refPt = srcPos;
23015 var intSqdist = sqdist(refPt, array2point(intersect));
23016 var labIntSqdist = sqdist(refPt, array2point(labelIntersect));
23017 var minSqDist = intSqdist;
23018
23019 if (labIntSqdist < intSqdist) {
23020 intersect = labelIntersect;
23021 minSqDist = labIntSqdist;
23022 }
23023
23024 if (labelIntersect.length > 2) {
23025 var labInt2SqDist = sqdist(refPt, {
23026 x: labelIntersect[2],
23027 y: labelIntersect[3]
23028 });
23029
23030 if (labInt2SqDist < minSqDist) {
23031 intersect = [labelIntersect[2], labelIntersect[3]];
23032 }
23033 }
23034 }
23035 }
23036 }
23037
23038 var arrowEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].spacing(edge) + tgtDist);
23039 var edgeEnd = shortenIntersection(intersect, p1, r.arrowShapes[tgtArShape].gap(edge) + tgtDist);
23040 rs.endX = edgeEnd[0];
23041 rs.endY = edgeEnd[1];
23042 rs.arrowEndX = arrowEnd[0];
23043 rs.arrowEndY = arrowEnd[1];
23044
23045 if (srcManEndptVal === 'inside-to-node') {
23046 intersect = [srcPos.x, srcPos.y];
23047 } else if (srcManEndpt.units) {
23048 intersect = this.manualEndptToPx(source, srcManEndpt);
23049 } else if (srcManEndptVal === 'outside-to-line') {
23050 intersect = rs.srcIntn; // use cached value from ctrlpt calc
23051 } else {
23052 if (srcManEndptVal === 'outside-to-node' || srcManEndptVal === 'outside-to-node-or-label') {
23053 p2_i = p2;
23054 } else if (srcManEndptVal === 'outside-to-line' || srcManEndptVal === 'outside-to-line-or-label') {
23055 p2_i = [tgtPos.x, tgtPos.y];
23056 }
23057
23058 intersect = r.nodeShapes[this.getNodeShape(source)].intersectLine(srcPos.x, srcPos.y, source.outerWidth(), source.outerHeight(), p2_i[0], p2_i[1], 0);
23059
23060 if (srcManEndptVal === 'outside-to-node-or-label' || srcManEndptVal === 'outside-to-line-or-label') {
23061 var srs = source._private.rscratch;
23062 var _lw = srs.labelWidth;
23063 var _lh = srs.labelHeight;
23064 var _lx = srs.labelX;
23065 var _ly = srs.labelY;
23066
23067 var _lw2 = _lw / 2;
23068
23069 var _lh2 = _lh / 2;
23070
23071 var _va = source.pstyle('text-valign').value;
23072
23073 if (_va === 'top') {
23074 _ly -= _lh2;
23075 } else if (_va === 'bottom') {
23076 _ly += _lh2;
23077 }
23078
23079 var _ha = source.pstyle('text-halign').value;
23080
23081 if (_ha === 'left') {
23082 _lx -= _lw2;
23083 } else if (_ha === 'right') {
23084 _lx += _lw2;
23085 }
23086
23087 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);
23088
23089 if (_labelIntersect.length > 0) {
23090 var _refPt = tgtPos;
23091
23092 var _intSqdist = sqdist(_refPt, array2point(intersect));
23093
23094 var _labIntSqdist = sqdist(_refPt, array2point(_labelIntersect));
23095
23096 var _minSqDist = _intSqdist;
23097
23098 if (_labIntSqdist < _intSqdist) {
23099 intersect = [_labelIntersect[0], _labelIntersect[1]];
23100 _minSqDist = _labIntSqdist;
23101 }
23102
23103 if (_labelIntersect.length > 2) {
23104 var _labInt2SqDist = sqdist(_refPt, {
23105 x: _labelIntersect[2],
23106 y: _labelIntersect[3]
23107 });
23108
23109 if (_labInt2SqDist < _minSqDist) {
23110 intersect = [_labelIntersect[2], _labelIntersect[3]];
23111 }
23112 }
23113 }
23114 }
23115 }
23116
23117 var arrowStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].spacing(edge) + srcDist);
23118 var edgeStart = shortenIntersection(intersect, p2, r.arrowShapes[srcArShape].gap(edge) + srcDist);
23119 rs.startX = edgeStart[0];
23120 rs.startY = edgeStart[1];
23121 rs.arrowStartX = arrowStart[0];
23122 rs.arrowStartY = arrowStart[1];
23123
23124 if (hasEndpts) {
23125 if (!number(rs.startX) || !number(rs.startY) || !number(rs.endX) || !number(rs.endY)) {
23126 rs.badLine = true;
23127 } else {
23128 rs.badLine = false;
23129 }
23130 }
23131};
23132
23133BRp$4.getSourceEndpoint = function (edge) {
23134 var rs = edge[0]._private.rscratch;
23135 this.recalculateRenderedStyle(edge);
23136
23137 switch (rs.edgeType) {
23138 case 'haystack':
23139 return {
23140 x: rs.haystackPts[0],
23141 y: rs.haystackPts[1]
23142 };
23143
23144 default:
23145 return {
23146 x: rs.arrowStartX,
23147 y: rs.arrowStartY
23148 };
23149 }
23150};
23151
23152BRp$4.getTargetEndpoint = function (edge) {
23153 var rs = edge[0]._private.rscratch;
23154 this.recalculateRenderedStyle(edge);
23155
23156 switch (rs.edgeType) {
23157 case 'haystack':
23158 return {
23159 x: rs.haystackPts[2],
23160 y: rs.haystackPts[3]
23161 };
23162
23163 default:
23164 return {
23165 x: rs.arrowEndX,
23166 y: rs.arrowEndY
23167 };
23168 }
23169};
23170
23171var BRp$5 = {};
23172
23173function pushBezierPts(r, edge, pts) {
23174 var qbezierAt$1 = function qbezierAt$1(p1, p2, p3, t) {
23175 return qbezierAt(p1, p2, p3, t);
23176 };
23177
23178 var _p = edge._private;
23179 var bpts = _p.rstyle.bezierPts;
23180
23181 for (var i = 0; i < r.bezierProjPcts.length; i++) {
23182 var p = r.bezierProjPcts[i];
23183 bpts.push({
23184 x: qbezierAt$1(pts[0], pts[2], pts[4], p),
23185 y: qbezierAt$1(pts[1], pts[3], pts[5], p)
23186 });
23187 }
23188}
23189
23190BRp$5.storeEdgeProjections = function (edge) {
23191 var _p = edge._private;
23192 var rs = _p.rscratch;
23193 var et = rs.edgeType; // clear the cached points state
23194
23195 _p.rstyle.bezierPts = null;
23196 _p.rstyle.linePts = null;
23197 _p.rstyle.haystackPts = null;
23198
23199 if (et === 'multibezier' || et === 'bezier' || et === 'self' || et === 'compound') {
23200 _p.rstyle.bezierPts = [];
23201
23202 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23203 pushBezierPts(this, edge, rs.allpts.slice(i, i + 6));
23204 }
23205 } else if (et === 'segments') {
23206 var lpts = _p.rstyle.linePts = [];
23207
23208 for (var i = 0; i + 1 < rs.allpts.length; i += 2) {
23209 lpts.push({
23210 x: rs.allpts[i],
23211 y: rs.allpts[i + 1]
23212 });
23213 }
23214 } else if (et === 'haystack') {
23215 var hpts = rs.haystackPts;
23216 _p.rstyle.haystackPts = [{
23217 x: hpts[0],
23218 y: hpts[1]
23219 }, {
23220 x: hpts[2],
23221 y: hpts[3]
23222 }];
23223 }
23224
23225 _p.rstyle.arrowWidth = this.getArrowWidth(edge.pstyle('width').pfValue, edge.pstyle('arrow-scale').value) * this.arrowShapeWidth;
23226};
23227
23228BRp$5.recalculateEdgeProjections = function (edges) {
23229 this.findEdgeControlPoints(edges);
23230};
23231
23232/* global document */
23233
23234var BRp$6 = {};
23235
23236BRp$6.recalculateNodeLabelProjection = function (node) {
23237 var content = node.pstyle('label').strValue;
23238
23239 if (emptyString(content)) {
23240 return;
23241 }
23242
23243 var textX, textY;
23244 var _p = node._private;
23245 var nodeWidth = node.width();
23246 var nodeHeight = node.height();
23247 var padding = node.padding();
23248 var nodePos = node.position();
23249 var textHalign = node.pstyle('text-halign').strValue;
23250 var textValign = node.pstyle('text-valign').strValue;
23251 var rs = _p.rscratch;
23252 var rstyle = _p.rstyle;
23253
23254 switch (textHalign) {
23255 case 'left':
23256 textX = nodePos.x - nodeWidth / 2 - padding;
23257 break;
23258
23259 case 'right':
23260 textX = nodePos.x + nodeWidth / 2 + padding;
23261 break;
23262
23263 default:
23264 // e.g. center
23265 textX = nodePos.x;
23266 }
23267
23268 switch (textValign) {
23269 case 'top':
23270 textY = nodePos.y - nodeHeight / 2 - padding;
23271 break;
23272
23273 case 'bottom':
23274 textY = nodePos.y + nodeHeight / 2 + padding;
23275 break;
23276
23277 default:
23278 // e.g. middle
23279 textY = nodePos.y;
23280 }
23281
23282 rs.labelX = textX;
23283 rs.labelY = textY;
23284 rstyle.labelX = textX;
23285 rstyle.labelY = textY;
23286 this.calculateLabelAngles(node);
23287 this.applyLabelDimensions(node);
23288};
23289
23290var lineAngleFromDelta = function lineAngleFromDelta(dx, dy) {
23291 var angle = Math.atan(dy / dx);
23292
23293 if (dx === 0 && angle < 0) {
23294 angle = angle * -1;
23295 }
23296
23297 return angle;
23298};
23299
23300var lineAngle = function lineAngle(p0, p1) {
23301 var dx = p1.x - p0.x;
23302 var dy = p1.y - p0.y;
23303 return lineAngleFromDelta(dx, dy);
23304};
23305
23306var bezierAngle = function bezierAngle(p0, p1, p2, t) {
23307 var t0 = bound(0, t - 0.001, 1);
23308 var t1 = bound(0, t + 0.001, 1);
23309 var lp0 = qbezierPtAt(p0, p1, p2, t0);
23310 var lp1 = qbezierPtAt(p0, p1, p2, t1);
23311 return lineAngle(lp0, lp1);
23312};
23313
23314BRp$6.recalculateEdgeLabelProjections = function (edge) {
23315 var p;
23316 var _p = edge._private;
23317 var rs = _p.rscratch;
23318 var r = this;
23319 var content = {
23320 mid: edge.pstyle('label').strValue,
23321 source: edge.pstyle('source-label').strValue,
23322 target: edge.pstyle('target-label').strValue
23323 };
23324
23325 if (content.mid || content.source || content.target) ; else {
23326 return; // no labels => no calcs
23327 } // add center point to style so bounding box calculations can use it
23328 //
23329
23330
23331 p = {
23332 x: rs.midX,
23333 y: rs.midY
23334 };
23335
23336 var setRs = function setRs(propName, prefix, value) {
23337 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23338 setPrefixedProperty(_p.rstyle, propName, prefix, value);
23339 };
23340
23341 setRs('labelX', null, p.x);
23342 setRs('labelY', null, p.y);
23343 var midAngle = lineAngleFromDelta(rs.midDispX, rs.midDispY);
23344 setRs('labelAutoAngle', null, midAngle);
23345
23346 var createControlPointInfo = function createControlPointInfo() {
23347 if (createControlPointInfo.cache) {
23348 return createControlPointInfo.cache;
23349 } // use cache so only 1x per edge
23350
23351
23352 var ctrlpts = []; // store each ctrlpt info init
23353
23354 for (var i = 0; i + 5 < rs.allpts.length; i += 4) {
23355 var p0 = {
23356 x: rs.allpts[i],
23357 y: rs.allpts[i + 1]
23358 };
23359 var p1 = {
23360 x: rs.allpts[i + 2],
23361 y: rs.allpts[i + 3]
23362 }; // ctrlpt
23363
23364 var p2 = {
23365 x: rs.allpts[i + 4],
23366 y: rs.allpts[i + 5]
23367 };
23368 ctrlpts.push({
23369 p0: p0,
23370 p1: p1,
23371 p2: p2,
23372 startDist: 0,
23373 length: 0,
23374 segments: []
23375 });
23376 }
23377
23378 var bpts = _p.rstyle.bezierPts;
23379 var nProjs = r.bezierProjPcts.length;
23380
23381 function addSegment(cp, p0, p1, t0, t1) {
23382 var length = dist(p0, p1);
23383 var prevSegment = cp.segments[cp.segments.length - 1];
23384 var segment = {
23385 p0: p0,
23386 p1: p1,
23387 t0: t0,
23388 t1: t1,
23389 startDist: prevSegment ? prevSegment.startDist + prevSegment.length : 0,
23390 length: length
23391 };
23392 cp.segments.push(segment);
23393 cp.length += length;
23394 } // update each ctrlpt with segment info
23395
23396
23397 for (var _i = 0; _i < ctrlpts.length; _i++) {
23398 var cp = ctrlpts[_i];
23399 var prevCp = ctrlpts[_i - 1];
23400
23401 if (prevCp) {
23402 cp.startDist = prevCp.startDist + prevCp.length;
23403 }
23404
23405 addSegment(cp, cp.p0, bpts[_i * nProjs], 0, r.bezierProjPcts[0]); // first
23406
23407 for (var j = 0; j < nProjs - 1; j++) {
23408 addSegment(cp, bpts[_i * nProjs + j], bpts[_i * nProjs + j + 1], r.bezierProjPcts[j], r.bezierProjPcts[j + 1]);
23409 }
23410
23411 addSegment(cp, bpts[_i * nProjs + nProjs - 1], cp.p2, r.bezierProjPcts[nProjs - 1], 1); // last
23412 }
23413
23414 return createControlPointInfo.cache = ctrlpts;
23415 };
23416
23417 var calculateEndProjection = function calculateEndProjection(prefix) {
23418 var angle;
23419 var isSrc = prefix === 'source';
23420
23421 if (!content[prefix]) {
23422 return;
23423 }
23424
23425 var offset = edge.pstyle(prefix + '-text-offset').pfValue;
23426
23427 switch (rs.edgeType) {
23428 case 'self':
23429 case 'compound':
23430 case 'bezier':
23431 case 'multibezier':
23432 {
23433 var cps = createControlPointInfo();
23434 var selected;
23435 var startDist = 0;
23436 var totalDist = 0; // find the segment we're on
23437
23438 for (var i = 0; i < cps.length; i++) {
23439 var _cp = cps[isSrc ? i : cps.length - 1 - i];
23440
23441 for (var j = 0; j < _cp.segments.length; j++) {
23442 var _seg = _cp.segments[isSrc ? j : _cp.segments.length - 1 - j];
23443 var lastSeg = i === cps.length - 1 && j === _cp.segments.length - 1;
23444 startDist = totalDist;
23445 totalDist += _seg.length;
23446
23447 if (totalDist >= offset || lastSeg) {
23448 selected = {
23449 cp: _cp,
23450 segment: _seg
23451 };
23452 break;
23453 }
23454 }
23455
23456 if (selected) {
23457 break;
23458 }
23459 }
23460
23461 var cp = selected.cp;
23462 var seg = selected.segment;
23463 var tSegment = (offset - startDist) / seg.length;
23464 var segDt = seg.t1 - seg.t0;
23465 var t = isSrc ? seg.t0 + segDt * tSegment : seg.t1 - segDt * tSegment;
23466 t = bound(0, t, 1);
23467 p = qbezierPtAt(cp.p0, cp.p1, cp.p2, t);
23468 angle = bezierAngle(cp.p0, cp.p1, cp.p2, t);
23469 break;
23470 }
23471
23472 case 'straight':
23473 case 'segments':
23474 case 'haystack':
23475 {
23476 var d = 0,
23477 di,
23478 d0;
23479 var p0, p1;
23480 var l = rs.allpts.length;
23481
23482 for (var _i2 = 0; _i2 + 3 < l; _i2 += 2) {
23483 if (isSrc) {
23484 p0 = {
23485 x: rs.allpts[_i2],
23486 y: rs.allpts[_i2 + 1]
23487 };
23488 p1 = {
23489 x: rs.allpts[_i2 + 2],
23490 y: rs.allpts[_i2 + 3]
23491 };
23492 } else {
23493 p0 = {
23494 x: rs.allpts[l - 2 - _i2],
23495 y: rs.allpts[l - 1 - _i2]
23496 };
23497 p1 = {
23498 x: rs.allpts[l - 4 - _i2],
23499 y: rs.allpts[l - 3 - _i2]
23500 };
23501 }
23502
23503 di = dist(p0, p1);
23504 d0 = d;
23505 d += di;
23506
23507 if (d >= offset) {
23508 break;
23509 }
23510 }
23511
23512 var pD = offset - d0;
23513
23514 var _t = pD / di;
23515
23516 _t = bound(0, _t, 1);
23517 p = lineAt(p0, p1, _t);
23518 angle = lineAngle(p0, p1);
23519 break;
23520 }
23521 }
23522
23523 setRs('labelX', prefix, p.x);
23524 setRs('labelY', prefix, p.y);
23525 setRs('labelAutoAngle', prefix, angle);
23526 };
23527
23528 calculateEndProjection('source');
23529 calculateEndProjection('target');
23530 this.applyLabelDimensions(edge);
23531};
23532
23533BRp$6.applyLabelDimensions = function (ele) {
23534 this.applyPrefixedLabelDimensions(ele);
23535
23536 if (ele.isEdge()) {
23537 this.applyPrefixedLabelDimensions(ele, 'source');
23538 this.applyPrefixedLabelDimensions(ele, 'target');
23539 }
23540};
23541
23542BRp$6.applyPrefixedLabelDimensions = function (ele, prefix) {
23543 var _p = ele._private;
23544 var text = this.getLabelText(ele, prefix);
23545 var labelDims = this.calculateLabelDimensions(ele, text);
23546 var lineHeight = ele.pstyle('line-height').pfValue;
23547 var textWrap = ele.pstyle('text-wrap').strValue;
23548 var lines = getPrefixedProperty(_p.rscratch, 'labelWrapCachedLines', prefix) || [];
23549 var numLines = textWrap !== 'wrap' ? 1 : Math.max(lines.length, 1);
23550 var normPerLineHeight = labelDims.height / numLines;
23551 var labelLineHeight = normPerLineHeight * lineHeight;
23552 var width = labelDims.width;
23553 var height = labelDims.height + (numLines - 1) * (lineHeight - 1) * normPerLineHeight;
23554 setPrefixedProperty(_p.rstyle, 'labelWidth', prefix, width);
23555 setPrefixedProperty(_p.rscratch, 'labelWidth', prefix, width);
23556 setPrefixedProperty(_p.rstyle, 'labelHeight', prefix, height);
23557 setPrefixedProperty(_p.rscratch, 'labelHeight', prefix, height);
23558 setPrefixedProperty(_p.rscratch, 'labelLineHeight', prefix, labelLineHeight);
23559};
23560
23561BRp$6.getLabelText = function (ele, prefix) {
23562 var _p = ele._private;
23563 var pfd = prefix ? prefix + '-' : '';
23564 var text = ele.pstyle(pfd + 'label').strValue;
23565 var textTransform = ele.pstyle('text-transform').value;
23566
23567 var rscratch = function rscratch(propName, value) {
23568 if (value) {
23569 setPrefixedProperty(_p.rscratch, propName, prefix, value);
23570 return value;
23571 } else {
23572 return getPrefixedProperty(_p.rscratch, propName, prefix);
23573 }
23574 }; // for empty text, skip all processing
23575
23576
23577 if (!text) {
23578 return '';
23579 }
23580
23581 if (textTransform == 'none') ; else if (textTransform == 'uppercase') {
23582 text = text.toUpperCase();
23583 } else if (textTransform == 'lowercase') {
23584 text = text.toLowerCase();
23585 }
23586
23587 var wrapStyle = ele.pstyle('text-wrap').value;
23588
23589 if (wrapStyle === 'wrap') {
23590 var labelKey = rscratch('labelKey'); // save recalc if the label is the same as before
23591
23592 if (labelKey != null && rscratch('labelWrapKey') === labelKey) {
23593 return rscratch('labelWrapCachedText');
23594 }
23595
23596 var zwsp = "\u200B";
23597 var lines = text.split('\n');
23598 var maxW = ele.pstyle('text-max-width').pfValue;
23599 var overflow = ele.pstyle('text-overflow-wrap').value;
23600 var overflowAny = overflow === 'anywhere';
23601 var wrappedLines = [];
23602 var wordsRegex = /[\s\u200b]+/;
23603 var wordSeparator = overflowAny ? '' : ' ';
23604
23605 for (var l = 0; l < lines.length; l++) {
23606 var line = lines[l];
23607 var lineDims = this.calculateLabelDimensions(ele, line);
23608 var lineW = lineDims.width;
23609
23610 if (overflowAny) {
23611 var processedLine = line.split('').join(zwsp);
23612 line = processedLine;
23613 }
23614
23615 if (lineW > maxW) {
23616 // line is too long
23617 var words = line.split(wordsRegex);
23618 var subline = '';
23619
23620 for (var w = 0; w < words.length; w++) {
23621 var word = words[w];
23622 var testLine = subline.length === 0 ? word : subline + wordSeparator + word;
23623 var testDims = this.calculateLabelDimensions(ele, testLine);
23624 var testW = testDims.width;
23625
23626 if (testW <= maxW) {
23627 // word fits on current line
23628 subline += word + wordSeparator;
23629 } else {
23630 // word starts new line
23631 if (subline) {
23632 wrappedLines.push(subline);
23633 }
23634
23635 subline = word + wordSeparator;
23636 }
23637 } // if there's remaining text, put it in a wrapped line
23638
23639
23640 if (!subline.match(/^[\s\u200b]+$/)) {
23641 wrappedLines.push(subline);
23642 }
23643 } else {
23644 // line is already short enough
23645 wrappedLines.push(line);
23646 }
23647 } // for
23648
23649
23650 rscratch('labelWrapCachedLines', wrappedLines);
23651 text = rscratch('labelWrapCachedText', wrappedLines.join('\n'));
23652 rscratch('labelWrapKey', labelKey);
23653 } else if (wrapStyle === 'ellipsis') {
23654 var _maxW = ele.pstyle('text-max-width').pfValue;
23655 var ellipsized = '';
23656 var ellipsis = "\u2026";
23657 var incLastCh = false;
23658
23659 if (this.calculateLabelDimensions(ele, text).width < _maxW) {
23660 // the label already fits
23661 return text;
23662 }
23663
23664 for (var i = 0; i < text.length; i++) {
23665 var widthWithNextCh = this.calculateLabelDimensions(ele, ellipsized + text[i] + ellipsis).width;
23666
23667 if (widthWithNextCh > _maxW) {
23668 break;
23669 }
23670
23671 ellipsized += text[i];
23672
23673 if (i === text.length - 1) {
23674 incLastCh = true;
23675 }
23676 }
23677
23678 if (!incLastCh) {
23679 ellipsized += ellipsis;
23680 }
23681
23682 return ellipsized;
23683 } // if ellipsize
23684
23685
23686 return text;
23687};
23688
23689BRp$6.getLabelJustification = function (ele) {
23690 var justification = ele.pstyle('text-justification').strValue;
23691 var textHalign = ele.pstyle('text-halign').strValue;
23692
23693 if (justification === 'auto') {
23694 if (ele.isNode()) {
23695 switch (textHalign) {
23696 case 'left':
23697 return 'right';
23698
23699 case 'right':
23700 return 'left';
23701
23702 default:
23703 return 'center';
23704 }
23705 } else {
23706 return 'center';
23707 }
23708 } else {
23709 return justification;
23710 }
23711};
23712
23713BRp$6.calculateLabelDimensions = function (ele, text) {
23714 var r = this;
23715 var cacheKey = hashString(text, ele._private.labelDimsKey);
23716 var cache = r.labelDimCache || (r.labelDimCache = []);
23717 var existingVal = cache[cacheKey];
23718
23719 if (existingVal != null) {
23720 return existingVal;
23721 }
23722
23723 var padding = 0; // add padding around text dims, as the measurement isn't that accurate
23724
23725 var fStyle = ele.pstyle('font-style').strValue;
23726 var size = ele.pstyle('font-size').pfValue;
23727 var family = ele.pstyle('font-family').strValue;
23728 var weight = ele.pstyle('font-weight').strValue;
23729 var canvas = this.labelCalcCanvas;
23730 var c2d = this.labelCalcCanvasContext;
23731
23732 if (!canvas) {
23733 canvas = this.labelCalcCanvas = document.createElement('canvas');
23734 c2d = this.labelCalcCanvasContext = canvas.getContext('2d');
23735 var ds = canvas.style;
23736 ds.position = 'absolute';
23737 ds.left = '-9999px';
23738 ds.top = '-9999px';
23739 ds.zIndex = '-1';
23740 ds.visibility = 'hidden';
23741 ds.pointerEvents = 'none';
23742 }
23743
23744 c2d.font = "".concat(fStyle, " ").concat(weight, " ").concat(size, "px ").concat(family);
23745 var width = 0;
23746 var height = 0;
23747 var lines = text.split('\n');
23748
23749 for (var i = 0; i < lines.length; i++) {
23750 var line = lines[i];
23751 var metrics = c2d.measureText(line);
23752 var w = Math.ceil(metrics.width);
23753 var h = size;
23754 width = Math.max(w, width);
23755 height += h;
23756 }
23757
23758 width += padding;
23759 height += padding;
23760 return cache[cacheKey] = {
23761 width: width,
23762 height: height
23763 };
23764};
23765
23766BRp$6.calculateLabelAngle = function (ele, prefix) {
23767 var _p = ele._private;
23768 var rs = _p.rscratch;
23769 var isEdge = ele.isEdge();
23770 var prefixDash = prefix ? prefix + '-' : '';
23771 var rot = ele.pstyle(prefixDash + 'text-rotation');
23772 var rotStr = rot.strValue;
23773
23774 if (rotStr === 'none') {
23775 return 0;
23776 } else if (isEdge && rotStr === 'autorotate') {
23777 return rs.labelAutoAngle;
23778 } else if (rotStr === 'autorotate') {
23779 return 0;
23780 } else {
23781 return rot.pfValue;
23782 }
23783};
23784
23785BRp$6.calculateLabelAngles = function (ele) {
23786 var r = this;
23787 var isEdge = ele.isEdge();
23788 var _p = ele._private;
23789 var rs = _p.rscratch;
23790 rs.labelAngle = r.calculateLabelAngle(ele);
23791
23792 if (isEdge) {
23793 rs.sourceLabelAngle = r.calculateLabelAngle(ele, 'source');
23794 rs.targetLabelAngle = r.calculateLabelAngle(ele, 'target');
23795 }
23796};
23797
23798var BRp$7 = {};
23799var TOO_SMALL_CUT_RECT = 28;
23800var warnedCutRect = false;
23801
23802BRp$7.getNodeShape = function (node) {
23803 var r = this;
23804 var shape = node.pstyle('shape').value;
23805
23806 if (shape === 'cutrectangle' && (node.width() < TOO_SMALL_CUT_RECT || node.height() < TOO_SMALL_CUT_RECT)) {
23807 if (!warnedCutRect) {
23808 warn('The `cutrectangle` node shape can not be used at small sizes so `rectangle` is used instead');
23809 warnedCutRect = true;
23810 }
23811
23812 return 'rectangle';
23813 }
23814
23815 if (node.isParent()) {
23816 if (shape === 'rectangle' || shape === 'roundrectangle' || shape === 'round-rectangle' || shape === 'cutrectangle' || shape === 'cut-rectangle' || shape === 'barrel') {
23817 return shape;
23818 } else {
23819 return 'rectangle';
23820 }
23821 }
23822
23823 if (shape === 'polygon') {
23824 var points = node.pstyle('shape-polygon-points').value;
23825 return r.nodeShapes.makePolygon(points).name;
23826 }
23827
23828 return shape;
23829};
23830
23831var BRp$8 = {};
23832
23833BRp$8.registerCalculationListeners = function () {
23834 var cy = this.cy;
23835 var elesToUpdate = cy.collection();
23836 var r = this;
23837
23838 var enqueue = function enqueue(eles) {
23839 var dirtyStyleCaches = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
23840 elesToUpdate.merge(eles);
23841
23842 if (dirtyStyleCaches) {
23843 for (var i = 0; i < eles.length; i++) {
23844 var ele = eles[i];
23845 var _p = ele._private;
23846 var rstyle = _p.rstyle;
23847 rstyle.clean = false;
23848 rstyle.cleanConnected = false;
23849 }
23850 }
23851 };
23852
23853 r.binder(cy).on('bounds.* dirty.*', function onDirtyBounds(e) {
23854 var ele = e.target;
23855 enqueue(ele);
23856 }).on('style.* background.*', function onDirtyStyle(e) {
23857 var ele = e.target;
23858 enqueue(ele, false);
23859 });
23860
23861 var updateEleCalcs = function updateEleCalcs(willDraw) {
23862 if (willDraw) {
23863 var fns = r.onUpdateEleCalcsFns; // because we need to have up-to-date style (e.g. stylesheet mappers)
23864 // before calculating rendered style (and pstyle might not be called yet)
23865
23866 elesToUpdate.cleanStyle();
23867
23868 for (var i = 0; i < elesToUpdate.length; i++) {
23869 var ele = elesToUpdate[i];
23870 var rstyle = ele._private.rstyle;
23871
23872 if (ele.isNode() && !rstyle.cleanConnected) {
23873 enqueue(ele.connectedEdges());
23874 rstyle.cleanConnected = true;
23875 }
23876 }
23877
23878 if (fns) {
23879 for (var _i = 0; _i < fns.length; _i++) {
23880 var fn = fns[_i];
23881 fn(willDraw, elesToUpdate);
23882 }
23883 }
23884
23885 r.recalculateRenderedStyle(elesToUpdate);
23886 elesToUpdate = cy.collection();
23887 }
23888 };
23889
23890 r.flushRenderedStyleQueue = function () {
23891 updateEleCalcs(true);
23892 };
23893
23894 r.beforeRender(updateEleCalcs, r.beforeRenderPriorities.eleCalcs);
23895};
23896
23897BRp$8.onUpdateEleCalcs = function (fn) {
23898 var fns = this.onUpdateEleCalcsFns = this.onUpdateEleCalcsFns || [];
23899 fns.push(fn);
23900};
23901
23902BRp$8.recalculateRenderedStyle = function (eles, useCache) {
23903 var isCleanConnected = function isCleanConnected(ele) {
23904 return ele._private.rstyle.cleanConnected;
23905 };
23906
23907 var edges = [];
23908 var nodes = []; // the renderer can't be used for calcs when destroyed, e.g. ele.boundingBox()
23909
23910 if (this.destroyed) {
23911 return;
23912 } // use cache by default for perf
23913
23914
23915 if (useCache === undefined) {
23916 useCache = true;
23917 }
23918
23919 for (var i = 0; i < eles.length; i++) {
23920 var ele = eles[i];
23921 var _p = ele._private;
23922 var rstyle = _p.rstyle; // an edge may be implicitly dirty b/c of one of its connected nodes
23923 // (and a request for recalc may come in between frames)
23924
23925 if (ele.isEdge() && (!isCleanConnected(ele.source()) || !isCleanConnected(ele.target()))) {
23926 rstyle.clean = false;
23927 } // only update if dirty and in graph
23928
23929
23930 if (useCache && rstyle.clean || ele.removed()) {
23931 continue;
23932 } // only update if not display: none
23933
23934
23935 if (ele.pstyle('display').value === 'none') {
23936 continue;
23937 }
23938
23939 if (_p.group === 'nodes') {
23940 nodes.push(ele);
23941 } else {
23942 // edges
23943 edges.push(ele);
23944 }
23945
23946 rstyle.clean = true;
23947 } // update node data from projections
23948
23949
23950 for (var _i2 = 0; _i2 < nodes.length; _i2++) {
23951 var _ele = nodes[_i2];
23952 var _p2 = _ele._private;
23953 var _rstyle = _p2.rstyle;
23954
23955 var pos = _ele.position();
23956
23957 this.recalculateNodeLabelProjection(_ele);
23958 _rstyle.nodeX = pos.x;
23959 _rstyle.nodeY = pos.y;
23960 _rstyle.nodeW = _ele.pstyle('width').pfValue;
23961 _rstyle.nodeH = _ele.pstyle('height').pfValue;
23962 }
23963
23964 this.recalculateEdgeProjections(edges); // update edge data from projections
23965
23966 for (var _i3 = 0; _i3 < edges.length; _i3++) {
23967 var _ele2 = edges[_i3];
23968 var _p3 = _ele2._private;
23969 var _rstyle2 = _p3.rstyle;
23970 var rs = _p3.rscratch; // update rstyle positions
23971
23972 _rstyle2.srcX = rs.arrowStartX;
23973 _rstyle2.srcY = rs.arrowStartY;
23974 _rstyle2.tgtX = rs.arrowEndX;
23975 _rstyle2.tgtY = rs.arrowEndY;
23976 _rstyle2.midX = rs.midX;
23977 _rstyle2.midY = rs.midY;
23978 _rstyle2.labelAngle = rs.labelAngle;
23979 _rstyle2.sourceLabelAngle = rs.sourceLabelAngle;
23980 _rstyle2.targetLabelAngle = rs.targetLabelAngle;
23981 }
23982};
23983
23984var BRp$9 = {};
23985
23986BRp$9.updateCachedGrabbedEles = function () {
23987 var eles = this.cachedZSortedEles;
23988
23989 if (!eles) {
23990 // just let this be recalculated on the next z sort tick
23991 return;
23992 }
23993
23994 eles.drag = [];
23995 eles.nondrag = [];
23996 var grabTargets = [];
23997
23998 for (var i = 0; i < eles.length; i++) {
23999 var ele = eles[i];
24000 var rs = ele._private.rscratch;
24001
24002 if (ele.grabbed() && !ele.isParent()) {
24003 grabTargets.push(ele);
24004 } else if (rs.inDragLayer) {
24005 eles.drag.push(ele);
24006 } else {
24007 eles.nondrag.push(ele);
24008 }
24009 } // put the grab target nodes last so it's on top of its neighbourhood
24010
24011
24012 for (var i = 0; i < grabTargets.length; i++) {
24013 var ele = grabTargets[i];
24014 eles.drag.push(ele);
24015 }
24016};
24017
24018BRp$9.invalidateCachedZSortedEles = function () {
24019 this.cachedZSortedEles = null;
24020};
24021
24022BRp$9.getCachedZSortedEles = function (forceRecalc) {
24023 if (forceRecalc || !this.cachedZSortedEles) {
24024 var eles = this.cy.mutableElements().toArray();
24025 eles.sort(zIndexSort);
24026 eles.interactive = eles.filter(function (ele) {
24027 return ele.interactive();
24028 });
24029 this.cachedZSortedEles = eles;
24030 this.updateCachedGrabbedEles();
24031 } else {
24032 eles = this.cachedZSortedEles;
24033 }
24034
24035 return eles;
24036};
24037
24038var BRp$a = {};
24039[BRp$1, BRp$2, BRp$3, BRp$4, BRp$5, BRp$6, BRp$7, BRp$8, BRp$9].forEach(function (props) {
24040 extend(BRp$a, props);
24041});
24042
24043var BRp$b = {};
24044
24045BRp$b.getCachedImage = function (url, crossOrigin, onLoad) {
24046 var r = this;
24047 var imageCache = r.imageCache = r.imageCache || {};
24048 var cache = imageCache[url];
24049
24050 if (cache) {
24051 if (!cache.image.complete) {
24052 cache.image.addEventListener('load', onLoad);
24053 }
24054
24055 return cache.image;
24056 } else {
24057 cache = imageCache[url] = imageCache[url] || {};
24058 var image = cache.image = new Image(); // eslint-disable-line no-undef
24059
24060 image.addEventListener('load', onLoad);
24061 image.addEventListener('error', function () {
24062 image.error = true;
24063 }); // #1582 safari doesn't load data uris with crossOrigin properly
24064 // https://bugs.webkit.org/show_bug.cgi?id=123978
24065
24066 var dataUriPrefix = 'data:';
24067 var isDataUri = url.substring(0, dataUriPrefix.length).toLowerCase() === dataUriPrefix;
24068
24069 if (!isDataUri) {
24070 image.crossOrigin = crossOrigin; // prevent tainted canvas
24071 }
24072
24073 image.src = url;
24074 return image;
24075 }
24076};
24077
24078var BRp$c = {};
24079/* global document, window, ResizeObserver, MutationObserver */
24080
24081BRp$c.registerBinding = function (target, event, handler, useCapture) {
24082 // eslint-disable-line no-unused-vars
24083 var args = Array.prototype.slice.apply(arguments, [1]); // copy
24084
24085 var b = this.binder(target);
24086 return b.on.apply(b, args);
24087};
24088
24089BRp$c.binder = function (tgt) {
24090 var r = this;
24091 var tgtIsDom = tgt === window || tgt === document || tgt === document.body || domElement(tgt);
24092
24093 if (r.supportsPassiveEvents == null) {
24094 // from https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md#feature-detection
24095 var supportsPassive = false;
24096
24097 try {
24098 var opts = Object.defineProperty({}, 'passive', {
24099 get: function get() {
24100 supportsPassive = true;
24101 return true;
24102 }
24103 });
24104 window.addEventListener('test', null, opts);
24105 } catch (err) {// not supported
24106 }
24107
24108 r.supportsPassiveEvents = supportsPassive;
24109 }
24110
24111 var on = function on(event, handler, useCapture) {
24112 var args = Array.prototype.slice.call(arguments);
24113
24114 if (tgtIsDom && r.supportsPassiveEvents) {
24115 // replace useCapture w/ opts obj
24116 args[2] = {
24117 capture: useCapture != null ? useCapture : false,
24118 passive: false,
24119 once: false
24120 };
24121 }
24122
24123 r.bindings.push({
24124 target: tgt,
24125 args: args
24126 });
24127 (tgt.addEventListener || tgt.on).apply(tgt, args);
24128 return this;
24129 };
24130
24131 return {
24132 on: on,
24133 addEventListener: on,
24134 addListener: on,
24135 bind: on
24136 };
24137};
24138
24139BRp$c.nodeIsDraggable = function (node) {
24140 return node && node.isNode() && !node.locked() && node.grabbable();
24141};
24142
24143BRp$c.nodeIsGrabbable = function (node) {
24144 return this.nodeIsDraggable(node) && node.interactive();
24145};
24146
24147BRp$c.load = function () {
24148 var r = this;
24149
24150 var isSelected = function isSelected(ele) {
24151 return ele.selected();
24152 };
24153
24154 var triggerEvents = function triggerEvents(target, names, e, position) {
24155 if (target == null) {
24156 target = r.cy;
24157 }
24158
24159 for (var i = 0; i < names.length; i++) {
24160 var name = names[i];
24161 target.emit({
24162 originalEvent: e,
24163 type: name,
24164 position: position
24165 });
24166 }
24167 };
24168
24169 var isMultSelKeyDown = function isMultSelKeyDown(e) {
24170 return e.shiftKey || e.metaKey || e.ctrlKey; // maybe e.altKey
24171 };
24172
24173 var allowPanningPassthrough = function allowPanningPassthrough(down, downs) {
24174 var allowPassthrough = true;
24175
24176 if (r.cy.hasCompoundNodes() && down && down.pannable()) {
24177 // a grabbable compound node below the ele => no passthrough panning
24178 for (var i = 0; downs && i < downs.length; i++) {
24179 var down = downs[i]; //if any parent node in event hierarchy isn't pannable, reject passthrough
24180
24181 if (down.isNode() && down.isParent() && !down.pannable()) {
24182 allowPassthrough = false;
24183 break;
24184 }
24185 }
24186 } else {
24187 allowPassthrough = true;
24188 }
24189
24190 return allowPassthrough;
24191 };
24192
24193 var setGrabbed = function setGrabbed(ele) {
24194 ele[0]._private.grabbed = true;
24195 };
24196
24197 var setFreed = function setFreed(ele) {
24198 ele[0]._private.grabbed = false;
24199 };
24200
24201 var setInDragLayer = function setInDragLayer(ele) {
24202 ele[0]._private.rscratch.inDragLayer = true;
24203 };
24204
24205 var setOutDragLayer = function setOutDragLayer(ele) {
24206 ele[0]._private.rscratch.inDragLayer = false;
24207 };
24208
24209 var setGrabTarget = function setGrabTarget(ele) {
24210 ele[0]._private.rscratch.isGrabTarget = true;
24211 };
24212
24213 var removeGrabTarget = function removeGrabTarget(ele) {
24214 ele[0]._private.rscratch.isGrabTarget = false;
24215 };
24216
24217 var addToDragList = function addToDragList(ele, opts) {
24218 var list = opts.addToList;
24219 var listHasEle = list.has(ele);
24220
24221 if (!listHasEle) {
24222 list.merge(ele);
24223 setGrabbed(ele);
24224 }
24225 }; // helper function to determine which child nodes and inner edges
24226 // of a compound node to be dragged as well as the grabbed and selected nodes
24227
24228
24229 var addDescendantsToDrag = function addDescendantsToDrag(node, opts) {
24230 if (!node.cy().hasCompoundNodes()) {
24231 return;
24232 }
24233
24234 if (opts.inDragLayer == null && opts.addToList == null) {
24235 return;
24236 } // nothing to do
24237
24238
24239 var innerNodes = node.descendants();
24240
24241 if (opts.inDragLayer) {
24242 innerNodes.forEach(setInDragLayer);
24243 innerNodes.connectedEdges().forEach(setInDragLayer);
24244 }
24245
24246 if (opts.addToList) {
24247 opts.addToList.unmerge(innerNodes);
24248 }
24249 }; // adds the given nodes and its neighbourhood to the drag layer
24250
24251
24252 var addNodesToDrag = function addNodesToDrag(nodes, opts) {
24253 opts = opts || {};
24254 var hasCompoundNodes = nodes.cy().hasCompoundNodes();
24255
24256 if (opts.inDragLayer) {
24257 nodes.forEach(setInDragLayer);
24258 nodes.neighborhood().stdFilter(function (ele) {
24259 return !hasCompoundNodes || ele.isEdge();
24260 }).forEach(setInDragLayer);
24261 }
24262
24263 if (opts.addToList) {
24264 nodes.forEach(function (ele) {
24265 addToDragList(ele, opts);
24266 });
24267 }
24268
24269 addDescendantsToDrag(nodes, opts); // always add to drag
24270 // also add nodes and edges related to the topmost ancestor
24271
24272 updateAncestorsInDragLayer(nodes, {
24273 inDragLayer: opts.inDragLayer
24274 });
24275 r.updateCachedGrabbedEles();
24276 };
24277
24278 var addNodeToDrag = addNodesToDrag;
24279
24280 var freeDraggedElements = function freeDraggedElements(grabbedEles) {
24281 if (!grabbedEles) {
24282 return;
24283 } // just go over all elements rather than doing a bunch of (possibly expensive) traversals
24284
24285
24286 r.getCachedZSortedEles().forEach(function (ele) {
24287 setFreed(ele);
24288 setOutDragLayer(ele);
24289 removeGrabTarget(ele);
24290 });
24291 r.updateCachedGrabbedEles();
24292 }; // helper function to determine which ancestor nodes and edges should go
24293 // to the drag layer (or should be removed from drag layer).
24294
24295
24296 var updateAncestorsInDragLayer = function updateAncestorsInDragLayer(node, opts) {
24297 if (opts.inDragLayer == null && opts.addToList == null) {
24298 return;
24299 } // nothing to do
24300
24301
24302 if (!node.cy().hasCompoundNodes()) {
24303 return;
24304 } // find top-level parent
24305
24306
24307 var parent = node.ancestors().orphans(); // no parent node: no nodes to add to the drag layer
24308
24309 if (parent.same(node)) {
24310 return;
24311 }
24312
24313 var nodes = parent.descendants().spawnSelf().merge(parent).unmerge(node).unmerge(node.descendants());
24314 var edges = nodes.connectedEdges();
24315
24316 if (opts.inDragLayer) {
24317 edges.forEach(setInDragLayer);
24318 nodes.forEach(setInDragLayer);
24319 }
24320
24321 if (opts.addToList) {
24322 nodes.forEach(function (ele) {
24323 addToDragList(ele, opts);
24324 });
24325 }
24326 };
24327
24328 var blurActiveDomElement = function blurActiveDomElement() {
24329 if (document.activeElement != null && document.activeElement.blur != null) {
24330 document.activeElement.blur();
24331 }
24332 };
24333
24334 var haveMutationsApi = typeof MutationObserver !== 'undefined';
24335 var haveResizeObserverApi = typeof ResizeObserver !== 'undefined'; // watch for when the cy container is removed from the dom
24336
24337 if (haveMutationsApi) {
24338 r.removeObserver = new MutationObserver(function (mutns) {
24339 // eslint-disable-line no-undef
24340 for (var i = 0; i < mutns.length; i++) {
24341 var mutn = mutns[i];
24342 var rNodes = mutn.removedNodes;
24343
24344 if (rNodes) {
24345 for (var j = 0; j < rNodes.length; j++) {
24346 var rNode = rNodes[j];
24347
24348 if (rNode === r.container) {
24349 r.destroy();
24350 break;
24351 }
24352 }
24353 }
24354 }
24355 });
24356
24357 if (r.container.parentNode) {
24358 r.removeObserver.observe(r.container.parentNode, {
24359 childList: true
24360 });
24361 }
24362 } else {
24363 r.registerBinding(r.container, 'DOMNodeRemoved', function (e) {
24364 // eslint-disable-line no-unused-vars
24365 r.destroy();
24366 });
24367 }
24368
24369 var onResize = util(function () {
24370 r.cy.resize();
24371 }, 100);
24372
24373 if (haveMutationsApi) {
24374 r.styleObserver = new MutationObserver(onResize); // eslint-disable-line no-undef
24375
24376 r.styleObserver.observe(r.container, {
24377 attributes: true
24378 });
24379 } // auto resize
24380
24381
24382 r.registerBinding(window, 'resize', onResize); // eslint-disable-line no-undef
24383
24384 if (haveResizeObserverApi) {
24385 r.resizeObserver = new ResizeObserver(onResize); // eslint-disable-line no-undef
24386
24387 r.resizeObserver.observe(r.container);
24388 }
24389
24390 var forEachUp = function forEachUp(domEle, fn) {
24391 while (domEle != null) {
24392 fn(domEle);
24393 domEle = domEle.parentNode;
24394 }
24395 };
24396
24397 var invalidateCoords = function invalidateCoords() {
24398 r.invalidateContainerClientCoordsCache();
24399 };
24400
24401 forEachUp(r.container, function (domEle) {
24402 r.registerBinding(domEle, 'transitionend', invalidateCoords);
24403 r.registerBinding(domEle, 'animationend', invalidateCoords);
24404 r.registerBinding(domEle, 'scroll', invalidateCoords);
24405 }); // stop right click menu from appearing on cy
24406
24407 r.registerBinding(r.container, 'contextmenu', function (e) {
24408 e.preventDefault();
24409 });
24410
24411 var inBoxSelection = function inBoxSelection() {
24412 return r.selection[4] !== 0;
24413 };
24414
24415 var eventInContainer = function eventInContainer(e) {
24416 // save cycles if mouse events aren't to be captured
24417 var containerPageCoords = r.findContainerClientCoords();
24418 var x = containerPageCoords[0];
24419 var y = containerPageCoords[1];
24420 var width = containerPageCoords[2];
24421 var height = containerPageCoords[3];
24422 var positions = e.touches ? e.touches : [e];
24423 var atLeastOnePosInside = false;
24424
24425 for (var i = 0; i < positions.length; i++) {
24426 var p = positions[i];
24427
24428 if (x <= p.clientX && p.clientX <= x + width && y <= p.clientY && p.clientY <= y + height) {
24429 atLeastOnePosInside = true;
24430 break;
24431 }
24432 }
24433
24434 if (!atLeastOnePosInside) {
24435 return false;
24436 }
24437
24438 var container = r.container;
24439 var target = e.target;
24440 var tParent = target.parentNode;
24441 var containerIsTarget = false;
24442
24443 while (tParent) {
24444 if (tParent === container) {
24445 containerIsTarget = true;
24446 break;
24447 }
24448
24449 tParent = tParent.parentNode;
24450 }
24451
24452 if (!containerIsTarget) {
24453 return false;
24454 } // if target is outisde cy container, then this event is not for us
24455
24456
24457 return true;
24458 }; // Primary key
24459
24460
24461 r.registerBinding(r.container, 'mousedown', function mousedownHandler(e) {
24462 if (!eventInContainer(e)) {
24463 return;
24464 }
24465
24466 e.preventDefault();
24467 blurActiveDomElement();
24468 r.hoverData.capture = true;
24469 r.hoverData.which = e.which;
24470 var cy = r.cy;
24471 var gpos = [e.clientX, e.clientY];
24472 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24473 var select = r.selection;
24474 var nears = r.findNearestElements(pos[0], pos[1], true, false);
24475 var near = nears[0];
24476 var draggedElements = r.dragData.possibleDragElements;
24477 r.hoverData.mdownPos = pos;
24478 r.hoverData.mdownGPos = gpos;
24479
24480 var checkForTaphold = function checkForTaphold() {
24481 r.hoverData.tapholdCancelled = false;
24482 clearTimeout(r.hoverData.tapholdTimeout);
24483 r.hoverData.tapholdTimeout = setTimeout(function () {
24484 if (r.hoverData.tapholdCancelled) {
24485 return;
24486 } else {
24487 var ele = r.hoverData.down;
24488
24489 if (ele) {
24490 ele.emit({
24491 originalEvent: e,
24492 type: 'taphold',
24493 position: {
24494 x: pos[0],
24495 y: pos[1]
24496 }
24497 });
24498 } else {
24499 cy.emit({
24500 originalEvent: e,
24501 type: 'taphold',
24502 position: {
24503 x: pos[0],
24504 y: pos[1]
24505 }
24506 });
24507 }
24508 }
24509 }, r.tapholdDuration);
24510 }; // Right click button
24511
24512
24513 if (e.which == 3) {
24514 r.hoverData.cxtStarted = true;
24515 var cxtEvt = {
24516 originalEvent: e,
24517 type: 'cxttapstart',
24518 position: {
24519 x: pos[0],
24520 y: pos[1]
24521 }
24522 };
24523
24524 if (near) {
24525 near.activate();
24526 near.emit(cxtEvt);
24527 r.hoverData.down = near;
24528 } else {
24529 cy.emit(cxtEvt);
24530 }
24531
24532 r.hoverData.downTime = new Date().getTime();
24533 r.hoverData.cxtDragged = false; // Primary button
24534 } else if (e.which == 1) {
24535 if (near) {
24536 near.activate();
24537 } // Element dragging
24538
24539
24540 {
24541 // If something is under the cursor and it is draggable, prepare to grab it
24542 if (near != null) {
24543 if (r.nodeIsGrabbable(near)) {
24544 var makeEvent = function makeEvent(type) {
24545 return {
24546 originalEvent: e,
24547 type: type,
24548 position: {
24549 x: pos[0],
24550 y: pos[1]
24551 }
24552 };
24553 };
24554
24555 var triggerGrab = function triggerGrab(ele) {
24556 ele.emit(makeEvent('grab'));
24557 };
24558
24559 setGrabTarget(near);
24560
24561 if (!near.selected()) {
24562 draggedElements = r.dragData.possibleDragElements = cy.collection();
24563 addNodeToDrag(near, {
24564 addToList: draggedElements
24565 });
24566 near.emit(makeEvent('grabon')).emit(makeEvent('grab'));
24567 } else {
24568 draggedElements = r.dragData.possibleDragElements = cy.collection();
24569 var selectedNodes = cy.$(function (ele) {
24570 return ele.isNode() && ele.selected() && r.nodeIsGrabbable(ele);
24571 });
24572 addNodesToDrag(selectedNodes, {
24573 addToList: draggedElements
24574 });
24575 near.emit(makeEvent('grabon'));
24576 selectedNodes.forEach(triggerGrab);
24577 }
24578
24579 r.redrawHint('eles', true);
24580 r.redrawHint('drag', true);
24581 }
24582 }
24583
24584 r.hoverData.down = near;
24585 r.hoverData.downs = nears;
24586 r.hoverData.downTime = new Date().getTime();
24587 }
24588 triggerEvents(near, ['mousedown', 'tapstart', 'vmousedown'], e, {
24589 x: pos[0],
24590 y: pos[1]
24591 });
24592
24593 if (near == null) {
24594 select[4] = 1;
24595 r.data.bgActivePosistion = {
24596 x: pos[0],
24597 y: pos[1]
24598 };
24599 r.redrawHint('select', true);
24600 r.redraw();
24601 } else if (near.pannable()) {
24602 select[4] = 1; // for future pan
24603 }
24604
24605 checkForTaphold();
24606 } // Initialize selection box coordinates
24607
24608
24609 select[0] = select[2] = pos[0];
24610 select[1] = select[3] = pos[1];
24611 }, false);
24612 r.registerBinding(window, 'mousemove', function mousemoveHandler(e) {
24613 // eslint-disable-line no-undef
24614 var capture = r.hoverData.capture;
24615
24616 if (!capture && !eventInContainer(e)) {
24617 return;
24618 }
24619
24620 var preventDefault = false;
24621 var cy = r.cy;
24622 var zoom = cy.zoom();
24623 var gpos = [e.clientX, e.clientY];
24624 var pos = r.projectIntoViewport(gpos[0], gpos[1]);
24625 var mdownPos = r.hoverData.mdownPos;
24626 var mdownGPos = r.hoverData.mdownGPos;
24627 var select = r.selection;
24628 var near = null;
24629
24630 if (!r.hoverData.draggingEles && !r.hoverData.dragging && !r.hoverData.selecting) {
24631 near = r.findNearestElement(pos[0], pos[1], true, false);
24632 }
24633
24634 var last = r.hoverData.last;
24635 var down = r.hoverData.down;
24636 var disp = [pos[0] - select[2], pos[1] - select[3]];
24637 var draggedElements = r.dragData.possibleDragElements;
24638 var isOverThresholdDrag;
24639
24640 if (mdownGPos) {
24641 var dx = gpos[0] - mdownGPos[0];
24642 var dx2 = dx * dx;
24643 var dy = gpos[1] - mdownGPos[1];
24644 var dy2 = dy * dy;
24645 var dist2 = dx2 + dy2;
24646 r.hoverData.isOverThresholdDrag = isOverThresholdDrag = dist2 >= r.desktopTapThreshold2;
24647 }
24648
24649 var multSelKeyDown = isMultSelKeyDown(e);
24650
24651 if (isOverThresholdDrag) {
24652 r.hoverData.tapholdCancelled = true;
24653 }
24654
24655 var updateDragDelta = function updateDragDelta() {
24656 var dragDelta = r.hoverData.dragDelta = r.hoverData.dragDelta || [];
24657
24658 if (dragDelta.length === 0) {
24659 dragDelta.push(disp[0]);
24660 dragDelta.push(disp[1]);
24661 } else {
24662 dragDelta[0] += disp[0];
24663 dragDelta[1] += disp[1];
24664 }
24665 };
24666
24667 preventDefault = true;
24668 triggerEvents(near, ['mousemove', 'vmousemove', 'tapdrag'], e, {
24669 x: pos[0],
24670 y: pos[1]
24671 });
24672
24673 var goIntoBoxMode = function goIntoBoxMode() {
24674 r.data.bgActivePosistion = undefined;
24675
24676 if (!r.hoverData.selecting) {
24677 cy.emit({
24678 originalEvent: e,
24679 type: 'boxstart',
24680 position: {
24681 x: pos[0],
24682 y: pos[1]
24683 }
24684 });
24685 }
24686
24687 select[4] = 1;
24688 r.hoverData.selecting = true;
24689 r.redrawHint('select', true);
24690 r.redraw();
24691 }; // trigger context drag if rmouse down
24692
24693
24694 if (r.hoverData.which === 3) {
24695 // but only if over threshold
24696 if (isOverThresholdDrag) {
24697 var cxtEvt = {
24698 originalEvent: e,
24699 type: 'cxtdrag',
24700 position: {
24701 x: pos[0],
24702 y: pos[1]
24703 }
24704 };
24705
24706 if (down) {
24707 down.emit(cxtEvt);
24708 } else {
24709 cy.emit(cxtEvt);
24710 }
24711
24712 r.hoverData.cxtDragged = true;
24713
24714 if (!r.hoverData.cxtOver || near !== r.hoverData.cxtOver) {
24715 if (r.hoverData.cxtOver) {
24716 r.hoverData.cxtOver.emit({
24717 originalEvent: e,
24718 type: 'cxtdragout',
24719 position: {
24720 x: pos[0],
24721 y: pos[1]
24722 }
24723 });
24724 }
24725
24726 r.hoverData.cxtOver = near;
24727
24728 if (near) {
24729 near.emit({
24730 originalEvent: e,
24731 type: 'cxtdragover',
24732 position: {
24733 x: pos[0],
24734 y: pos[1]
24735 }
24736 });
24737 }
24738 }
24739 } // Check if we are drag panning the entire graph
24740
24741 } else if (r.hoverData.dragging) {
24742 preventDefault = true;
24743
24744 if (cy.panningEnabled() && cy.userPanningEnabled()) {
24745 var deltaP;
24746
24747 if (r.hoverData.justStartedPan) {
24748 var mdPos = r.hoverData.mdownPos;
24749 deltaP = {
24750 x: (pos[0] - mdPos[0]) * zoom,
24751 y: (pos[1] - mdPos[1]) * zoom
24752 };
24753 r.hoverData.justStartedPan = false;
24754 } else {
24755 deltaP = {
24756 x: disp[0] * zoom,
24757 y: disp[1] * zoom
24758 };
24759 }
24760
24761 cy.panBy(deltaP);
24762 cy.emit('dragpan');
24763 r.hoverData.dragged = true;
24764 } // Needs reproject due to pan changing viewport
24765
24766
24767 pos = r.projectIntoViewport(e.clientX, e.clientY); // Checks primary button down & out of time & mouse not moved much
24768 } else if (select[4] == 1 && (down == null || down.pannable())) {
24769 if (isOverThresholdDrag) {
24770 if (!r.hoverData.dragging && cy.boxSelectionEnabled() && (multSelKeyDown || !cy.panningEnabled() || !cy.userPanningEnabled())) {
24771 goIntoBoxMode();
24772 } else if (!r.hoverData.selecting && cy.panningEnabled() && cy.userPanningEnabled()) {
24773 var allowPassthrough = allowPanningPassthrough(down, r.hoverData.downs);
24774
24775 if (allowPassthrough) {
24776 r.hoverData.dragging = true;
24777 r.hoverData.justStartedPan = true;
24778 select[4] = 0;
24779 r.data.bgActivePosistion = array2point(mdownPos);
24780 r.redrawHint('select', true);
24781 r.redraw();
24782 }
24783 }
24784
24785 if (down && down.pannable() && down.active()) {
24786 down.unactivate();
24787 }
24788 }
24789 } else {
24790 if (down && down.pannable() && down.active()) {
24791 down.unactivate();
24792 }
24793
24794 if ((!down || !down.grabbed()) && near != last) {
24795 if (last) {
24796 triggerEvents(last, ['mouseout', 'tapdragout'], e, {
24797 x: pos[0],
24798 y: pos[1]
24799 });
24800 }
24801
24802 if (near) {
24803 triggerEvents(near, ['mouseover', 'tapdragover'], e, {
24804 x: pos[0],
24805 y: pos[1]
24806 });
24807 }
24808
24809 r.hoverData.last = near;
24810 }
24811
24812 if (down) {
24813 if (isOverThresholdDrag) {
24814 // then we can take action
24815 if (cy.boxSelectionEnabled() && multSelKeyDown) {
24816 // then selection overrides
24817 if (down && down.grabbed()) {
24818 freeDraggedElements(draggedElements);
24819 down.emit('freeon');
24820 draggedElements.emit('free');
24821
24822 if (r.dragData.didDrag) {
24823 down.emit('dragfreeon');
24824 draggedElements.emit('dragfree');
24825 }
24826 }
24827
24828 goIntoBoxMode();
24829 } else if (down && down.grabbed() && r.nodeIsDraggable(down)) {
24830 // drag node
24831 var justStartedDrag = !r.dragData.didDrag;
24832
24833 if (justStartedDrag) {
24834 r.redrawHint('eles', true);
24835 }
24836
24837 r.dragData.didDrag = true; // indicate that we actually did drag the node
24838
24839 var toTrigger = cy.collection(); // now, add the elements to the drag layer if not done already
24840
24841 if (!r.hoverData.draggingEles) {
24842 addNodesToDrag(draggedElements, {
24843 inDragLayer: true
24844 });
24845 }
24846
24847 var totalShift = {
24848 x: 0,
24849 y: 0
24850 };
24851
24852 if (number(disp[0]) && number(disp[1])) {
24853 totalShift.x += disp[0];
24854 totalShift.y += disp[1];
24855
24856 if (justStartedDrag) {
24857 var dragDelta = r.hoverData.dragDelta;
24858
24859 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
24860 totalShift.x += dragDelta[0];
24861 totalShift.y += dragDelta[1];
24862 }
24863 }
24864 }
24865
24866 for (var i = 0; i < draggedElements.length; i++) {
24867 var dEle = draggedElements[i];
24868
24869 if (r.nodeIsDraggable(dEle) && dEle.grabbed()) {
24870 toTrigger.push(dEle);
24871 }
24872 }
24873
24874 r.hoverData.draggingEles = true;
24875 toTrigger.silentShift(totalShift).emit('position drag');
24876 r.redrawHint('drag', true);
24877 r.redraw();
24878 }
24879 } else {
24880 // otherwise save drag delta for when we actually start dragging so the relative grab pos is constant
24881 updateDragDelta();
24882 }
24883 } // prevent the dragging from triggering text selection on the page
24884
24885
24886 preventDefault = true;
24887 }
24888
24889 select[2] = pos[0];
24890 select[3] = pos[1];
24891
24892 if (preventDefault) {
24893 if (e.stopPropagation) e.stopPropagation();
24894 if (e.preventDefault) e.preventDefault();
24895 return false;
24896 }
24897 }, false);
24898 r.registerBinding(window, 'mouseup', function mouseupHandler(e) {
24899 // eslint-disable-line no-undef
24900 var capture = r.hoverData.capture;
24901
24902 if (!capture) {
24903 return;
24904 }
24905
24906 r.hoverData.capture = false;
24907 var cy = r.cy;
24908 var pos = r.projectIntoViewport(e.clientX, e.clientY);
24909 var select = r.selection;
24910 var near = r.findNearestElement(pos[0], pos[1], true, false);
24911 var draggedElements = r.dragData.possibleDragElements;
24912 var down = r.hoverData.down;
24913 var multSelKeyDown = isMultSelKeyDown(e);
24914
24915 if (r.data.bgActivePosistion) {
24916 r.redrawHint('select', true);
24917 r.redraw();
24918 }
24919
24920 r.hoverData.tapholdCancelled = true;
24921 r.data.bgActivePosistion = undefined; // not active bg now
24922
24923 if (down) {
24924 down.unactivate();
24925 }
24926
24927 if (r.hoverData.which === 3) {
24928 var cxtEvt = {
24929 originalEvent: e,
24930 type: 'cxttapend',
24931 position: {
24932 x: pos[0],
24933 y: pos[1]
24934 }
24935 };
24936
24937 if (down) {
24938 down.emit(cxtEvt);
24939 } else {
24940 cy.emit(cxtEvt);
24941 }
24942
24943 if (!r.hoverData.cxtDragged) {
24944 var cxtTap = {
24945 originalEvent: e,
24946 type: 'cxttap',
24947 position: {
24948 x: pos[0],
24949 y: pos[1]
24950 }
24951 };
24952
24953 if (down) {
24954 down.emit(cxtTap);
24955 } else {
24956 cy.emit(cxtTap);
24957 }
24958 }
24959
24960 r.hoverData.cxtDragged = false;
24961 r.hoverData.which = null;
24962 } else if (r.hoverData.which === 1) {
24963 triggerEvents(near, ['mouseup', 'tapend', 'vmouseup'], e, {
24964 x: pos[0],
24965 y: pos[1]
24966 });
24967
24968 if (!r.dragData.didDrag // didn't move a node around
24969 && !r.hoverData.dragged // didn't pan
24970 && !r.hoverData.selecting // not box selection
24971 && !r.hoverData.isOverThresholdDrag // didn't move too much
24972 ) {
24973 triggerEvents(down, ['click', 'tap', 'vclick'], e, {
24974 x: pos[0],
24975 y: pos[1]
24976 });
24977 } // Deselect all elements if nothing is currently under the mouse cursor and we aren't dragging something
24978
24979
24980 if (down == null && // not mousedown on node
24981 !r.dragData.didDrag // didn't move the node around
24982 && !r.hoverData.selecting // not box selection
24983 && !r.hoverData.dragged // didn't pan
24984 && !isMultSelKeyDown(e)) {
24985 cy.$(isSelected).unselect(['tapunselect']);
24986
24987 if (draggedElements.length > 0) {
24988 r.redrawHint('eles', true);
24989 }
24990
24991 r.dragData.possibleDragElements = draggedElements = cy.collection();
24992 } // Single selection
24993
24994
24995 if (near == down && !r.dragData.didDrag && !r.hoverData.selecting) {
24996 if (near != null && near._private.selectable) {
24997 if (r.hoverData.dragging) ; else if (cy.selectionType() === 'additive' || multSelKeyDown) {
24998 if (near.selected()) {
24999 near.unselect(['tapunselect']);
25000 } else {
25001 near.select(['tapselect']);
25002 }
25003 } else {
25004 if (!multSelKeyDown) {
25005 cy.$(isSelected).unmerge(near).unselect(['tapunselect']);
25006 near.select(['tapselect']);
25007 }
25008 }
25009
25010 r.redrawHint('eles', true);
25011 }
25012 }
25013
25014 if (r.hoverData.selecting) {
25015 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25016 r.redrawHint('select', true);
25017
25018 if (box.length > 0) {
25019 r.redrawHint('eles', true);
25020 }
25021
25022 cy.emit({
25023 type: 'boxend',
25024 originalEvent: e,
25025 position: {
25026 x: pos[0],
25027 y: pos[1]
25028 }
25029 });
25030
25031 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25032 return ele.selectable() && !ele.selected();
25033 };
25034
25035 if (cy.selectionType() === 'additive') {
25036 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25037 } else {
25038 if (!multSelKeyDown) {
25039 cy.$(isSelected).unmerge(box).unselect();
25040 }
25041
25042 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25043 } // always need redraw in case eles unselectable
25044
25045
25046 r.redraw();
25047 } // Cancel drag pan
25048
25049
25050 if (r.hoverData.dragging) {
25051 r.hoverData.dragging = false;
25052 r.redrawHint('select', true);
25053 r.redrawHint('eles', true);
25054 r.redraw();
25055 }
25056
25057 if (!select[4]) {
25058 r.redrawHint('drag', true);
25059 r.redrawHint('eles', true);
25060 var downWasGrabbed = down && down.grabbed();
25061 freeDraggedElements(draggedElements);
25062
25063 if (downWasGrabbed) {
25064 down.emit('freeon');
25065 draggedElements.emit('free');
25066
25067 if (r.dragData.didDrag) {
25068 down.emit('dragfreeon');
25069 draggedElements.emit('dragfree');
25070 }
25071 }
25072 }
25073 } // else not right mouse
25074
25075
25076 select[4] = 0;
25077 r.hoverData.down = null;
25078 r.hoverData.cxtStarted = false;
25079 r.hoverData.draggingEles = false;
25080 r.hoverData.selecting = false;
25081 r.hoverData.isOverThresholdDrag = false;
25082 r.dragData.didDrag = false;
25083 r.hoverData.dragged = false;
25084 r.hoverData.dragDelta = [];
25085 r.hoverData.mdownPos = null;
25086 r.hoverData.mdownGPos = null;
25087 }, false);
25088
25089 var wheelHandler = function wheelHandler(e) {
25090 if (r.scrollingPage) {
25091 return;
25092 } // while scrolling, ignore wheel-to-zoom
25093
25094
25095 var cy = r.cy;
25096 var zoom = cy.zoom();
25097 var pan = cy.pan();
25098 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25099 var rpos = [pos[0] * zoom + pan.x, pos[1] * zoom + pan.y];
25100
25101 if (r.hoverData.draggingEles || r.hoverData.dragging || r.hoverData.cxtStarted || inBoxSelection()) {
25102 // if pan dragging or cxt dragging, wheel movements make no zoom
25103 e.preventDefault();
25104 return;
25105 }
25106
25107 if (cy.panningEnabled() && cy.userPanningEnabled() && cy.zoomingEnabled() && cy.userZoomingEnabled()) {
25108 e.preventDefault();
25109 r.data.wheelZooming = true;
25110 clearTimeout(r.data.wheelTimeout);
25111 r.data.wheelTimeout = setTimeout(function () {
25112 r.data.wheelZooming = false;
25113 r.redrawHint('eles', true);
25114 r.redraw();
25115 }, 150);
25116 var diff;
25117
25118 if (e.deltaY != null) {
25119 diff = e.deltaY / -250;
25120 } else if (e.wheelDeltaY != null) {
25121 diff = e.wheelDeltaY / 1000;
25122 } else {
25123 diff = e.wheelDelta / 1000;
25124 }
25125
25126 diff = diff * r.wheelSensitivity;
25127 var needsWheelFix = e.deltaMode === 1;
25128
25129 if (needsWheelFix) {
25130 // fixes slow wheel events on ff/linux and ff/windows
25131 diff *= 33;
25132 }
25133
25134 var newZoom = cy.zoom() * Math.pow(10, diff);
25135
25136 if (e.type === 'gesturechange') {
25137 newZoom = r.gestureStartZoom * e.scale;
25138 }
25139
25140 cy.zoom({
25141 level: newZoom,
25142 renderedPosition: {
25143 x: rpos[0],
25144 y: rpos[1]
25145 }
25146 });
25147 cy.emit(e.type === 'gesturechange' ? 'pinchzoom' : 'scrollzoom');
25148 }
25149 }; // Functions to help with whether mouse wheel should trigger zooming
25150 // --
25151
25152
25153 r.registerBinding(r.container, 'wheel', wheelHandler, true); // disable nonstandard wheel events
25154 // r.registerBinding(r.container, 'mousewheel', wheelHandler, true);
25155 // r.registerBinding(r.container, 'DOMMouseScroll', wheelHandler, true);
25156 // r.registerBinding(r.container, 'MozMousePixelScroll', wheelHandler, true); // older firefox
25157
25158 r.registerBinding(window, 'scroll', function scrollHandler(e) {
25159 // eslint-disable-line no-unused-vars
25160 r.scrollingPage = true;
25161 clearTimeout(r.scrollingPageTimeout);
25162 r.scrollingPageTimeout = setTimeout(function () {
25163 r.scrollingPage = false;
25164 }, 250);
25165 }, true); // desktop safari pinch to zoom start
25166
25167 r.registerBinding(r.container, 'gesturestart', function gestureStartHandler(e) {
25168 r.gestureStartZoom = r.cy.zoom();
25169
25170 if (!r.hasTouchStarted) {
25171 // don't affect touch devices like iphone
25172 e.preventDefault();
25173 }
25174 }, true);
25175 r.registerBinding(r.container, 'gesturechange', function (e) {
25176 if (!r.hasTouchStarted) {
25177 // don't affect touch devices like iphone
25178 wheelHandler(e);
25179 }
25180 }, true); // Functions to help with handling mouseout/mouseover on the Cytoscape container
25181 // Handle mouseout on Cytoscape container
25182
25183 r.registerBinding(r.container, 'mouseout', function mouseOutHandler(e) {
25184 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25185 r.cy.emit({
25186 originalEvent: e,
25187 type: 'mouseout',
25188 position: {
25189 x: pos[0],
25190 y: pos[1]
25191 }
25192 });
25193 }, false);
25194 r.registerBinding(r.container, 'mouseover', function mouseOverHandler(e) {
25195 var pos = r.projectIntoViewport(e.clientX, e.clientY);
25196 r.cy.emit({
25197 originalEvent: e,
25198 type: 'mouseover',
25199 position: {
25200 x: pos[0],
25201 y: pos[1]
25202 }
25203 });
25204 }, false);
25205 var f1x1, f1y1, f2x1, f2y1; // starting points for pinch-to-zoom
25206
25207 var distance1, distance1Sq; // initial distance between finger 1 and finger 2 for pinch-to-zoom
25208
25209 var center1, modelCenter1; // center point on start pinch to zoom
25210
25211 var offsetLeft, offsetTop;
25212 var containerWidth, containerHeight;
25213 var twoFingersStartInside;
25214
25215 var distance = function distance(x1, y1, x2, y2) {
25216 return Math.sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1));
25217 };
25218
25219 var distanceSq = function distanceSq(x1, y1, x2, y2) {
25220 return (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
25221 };
25222
25223 var touchstartHandler;
25224 r.registerBinding(r.container, 'touchstart', touchstartHandler = function touchstartHandler(e) {
25225 r.hasTouchStarted = true;
25226
25227 if (!eventInContainer(e)) {
25228 return;
25229 }
25230
25231 blurActiveDomElement();
25232 r.touchData.capture = true;
25233 r.data.bgActivePosistion = undefined;
25234 var cy = r.cy;
25235 var now = r.touchData.now;
25236 var earlier = r.touchData.earlier;
25237
25238 if (e.touches[0]) {
25239 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25240 now[0] = pos[0];
25241 now[1] = pos[1];
25242 }
25243
25244 if (e.touches[1]) {
25245 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25246 now[2] = pos[0];
25247 now[3] = pos[1];
25248 }
25249
25250 if (e.touches[2]) {
25251 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25252 now[4] = pos[0];
25253 now[5] = pos[1];
25254 } // record starting points for pinch-to-zoom
25255
25256
25257 if (e.touches[1]) {
25258 r.touchData.singleTouchMoved = true;
25259 freeDraggedElements(r.dragData.touchDragEles);
25260 var offsets = r.findContainerClientCoords();
25261 offsetLeft = offsets[0];
25262 offsetTop = offsets[1];
25263 containerWidth = offsets[2];
25264 containerHeight = offsets[3];
25265 f1x1 = e.touches[0].clientX - offsetLeft;
25266 f1y1 = e.touches[0].clientY - offsetTop;
25267 f2x1 = e.touches[1].clientX - offsetLeft;
25268 f2y1 = e.touches[1].clientY - offsetTop;
25269 twoFingersStartInside = 0 <= f1x1 && f1x1 <= containerWidth && 0 <= f2x1 && f2x1 <= containerWidth && 0 <= f1y1 && f1y1 <= containerHeight && 0 <= f2y1 && f2y1 <= containerHeight;
25270 var pan = cy.pan();
25271 var zoom = cy.zoom();
25272 distance1 = distance(f1x1, f1y1, f2x1, f2y1);
25273 distance1Sq = distanceSq(f1x1, f1y1, f2x1, f2y1);
25274 center1 = [(f1x1 + f2x1) / 2, (f1y1 + f2y1) / 2];
25275 modelCenter1 = [(center1[0] - pan.x) / zoom, (center1[1] - pan.y) / zoom]; // consider context tap
25276
25277 var cxtDistThreshold = 200;
25278 var cxtDistThresholdSq = cxtDistThreshold * cxtDistThreshold;
25279
25280 if (distance1Sq < cxtDistThresholdSq && !e.touches[2]) {
25281 var near1 = r.findNearestElement(now[0], now[1], true, true);
25282 var near2 = r.findNearestElement(now[2], now[3], true, true);
25283
25284 if (near1 && near1.isNode()) {
25285 near1.activate().emit({
25286 originalEvent: e,
25287 type: 'cxttapstart',
25288 position: {
25289 x: now[0],
25290 y: now[1]
25291 }
25292 });
25293 r.touchData.start = near1;
25294 } else if (near2 && near2.isNode()) {
25295 near2.activate().emit({
25296 originalEvent: e,
25297 type: 'cxttapstart',
25298 position: {
25299 x: now[0],
25300 y: now[1]
25301 }
25302 });
25303 r.touchData.start = near2;
25304 } else {
25305 cy.emit({
25306 originalEvent: e,
25307 type: 'cxttapstart',
25308 position: {
25309 x: now[0],
25310 y: now[1]
25311 }
25312 });
25313 }
25314
25315 if (r.touchData.start) {
25316 r.touchData.start._private.grabbed = false;
25317 }
25318
25319 r.touchData.cxt = true;
25320 r.touchData.cxtDragged = false;
25321 r.data.bgActivePosistion = undefined;
25322 r.redraw();
25323 return;
25324 }
25325 }
25326
25327 if (e.touches[2]) {
25328 // ignore
25329 // safari on ios pans the page otherwise (normally you should be able to preventdefault on touchmove...)
25330 if (cy.boxSelectionEnabled()) {
25331 e.preventDefault();
25332 }
25333 } else if (e.touches[1]) ; else if (e.touches[0]) {
25334 var nears = r.findNearestElements(now[0], now[1], true, true);
25335 var near = nears[0];
25336
25337 if (near != null) {
25338 near.activate();
25339 r.touchData.start = near;
25340 r.touchData.starts = nears;
25341
25342 if (r.nodeIsGrabbable(near)) {
25343 var draggedEles = r.dragData.touchDragEles = cy.collection();
25344 var selectedNodes = null;
25345 r.redrawHint('eles', true);
25346 r.redrawHint('drag', true);
25347
25348 if (near.selected()) {
25349 // reset drag elements, since near will be added again
25350 selectedNodes = cy.$(function (ele) {
25351 return ele.selected() && r.nodeIsGrabbable(ele);
25352 });
25353 addNodesToDrag(selectedNodes, {
25354 addToList: draggedEles
25355 });
25356 } else {
25357 addNodeToDrag(near, {
25358 addToList: draggedEles
25359 });
25360 }
25361
25362 setGrabTarget(near);
25363
25364 var makeEvent = function makeEvent(type) {
25365 return {
25366 originalEvent: e,
25367 type: type,
25368 position: {
25369 x: now[0],
25370 y: now[1]
25371 }
25372 };
25373 };
25374
25375 near.emit(makeEvent('grabon'));
25376
25377 if (selectedNodes) {
25378 selectedNodes.forEach(function (n) {
25379 n.emit(makeEvent('grab'));
25380 });
25381 } else {
25382 near.emit(makeEvent('grab'));
25383 }
25384 }
25385 }
25386
25387 triggerEvents(near, ['touchstart', 'tapstart', 'vmousedown'], e, {
25388 x: now[0],
25389 y: now[1]
25390 });
25391
25392 if (near == null) {
25393 r.data.bgActivePosistion = {
25394 x: pos[0],
25395 y: pos[1]
25396 };
25397 r.redrawHint('select', true);
25398 r.redraw();
25399 } // Tap, taphold
25400 // -----
25401
25402
25403 r.touchData.singleTouchMoved = false;
25404 r.touchData.singleTouchStartTime = +new Date();
25405 clearTimeout(r.touchData.tapholdTimeout);
25406 r.touchData.tapholdTimeout = setTimeout(function () {
25407 if (r.touchData.singleTouchMoved === false && !r.pinching // if pinching, then taphold unselect shouldn't take effect
25408 && !r.touchData.selecting // box selection shouldn't allow taphold through
25409 ) {
25410 triggerEvents(r.touchData.start, ['taphold'], e, {
25411 x: now[0],
25412 y: now[1]
25413 });
25414 }
25415 }, r.tapholdDuration);
25416 }
25417
25418 if (e.touches.length >= 1) {
25419 var sPos = r.touchData.startPosition = [];
25420
25421 for (var i = 0; i < now.length; i++) {
25422 sPos[i] = earlier[i] = now[i];
25423 }
25424
25425 var touch0 = e.touches[0];
25426 r.touchData.startGPosition = [touch0.clientX, touch0.clientY];
25427 }
25428 }, false);
25429 var touchmoveHandler;
25430 r.registerBinding(window, 'touchmove', touchmoveHandler = function touchmoveHandler(e) {
25431 // eslint-disable-line no-undef
25432 var capture = r.touchData.capture;
25433
25434 if (!capture && !eventInContainer(e)) {
25435 return;
25436 }
25437
25438 var select = r.selection;
25439 var cy = r.cy;
25440 var now = r.touchData.now;
25441 var earlier = r.touchData.earlier;
25442 var zoom = cy.zoom();
25443
25444 if (e.touches[0]) {
25445 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25446 now[0] = pos[0];
25447 now[1] = pos[1];
25448 }
25449
25450 if (e.touches[1]) {
25451 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25452 now[2] = pos[0];
25453 now[3] = pos[1];
25454 }
25455
25456 if (e.touches[2]) {
25457 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25458 now[4] = pos[0];
25459 now[5] = pos[1];
25460 }
25461
25462 var startGPos = r.touchData.startGPosition;
25463 var isOverThresholdDrag;
25464
25465 if (capture && e.touches[0] && startGPos) {
25466 var disp = [];
25467
25468 for (var j = 0; j < now.length; j++) {
25469 disp[j] = now[j] - earlier[j];
25470 }
25471
25472 var dx = e.touches[0].clientX - startGPos[0];
25473 var dx2 = dx * dx;
25474 var dy = e.touches[0].clientY - startGPos[1];
25475 var dy2 = dy * dy;
25476 var dist2 = dx2 + dy2;
25477 isOverThresholdDrag = dist2 >= r.touchTapThreshold2;
25478 } // context swipe cancelling
25479
25480
25481 if (capture && r.touchData.cxt) {
25482 e.preventDefault();
25483 var f1x2 = e.touches[0].clientX - offsetLeft,
25484 f1y2 = e.touches[0].clientY - offsetTop;
25485 var f2x2 = e.touches[1].clientX - offsetLeft,
25486 f2y2 = e.touches[1].clientY - offsetTop; // var distance2 = distance( f1x2, f1y2, f2x2, f2y2 );
25487
25488 var distance2Sq = distanceSq(f1x2, f1y2, f2x2, f2y2);
25489 var factorSq = distance2Sq / distance1Sq;
25490 var distThreshold = 150;
25491 var distThresholdSq = distThreshold * distThreshold;
25492 var factorThreshold = 1.5;
25493 var factorThresholdSq = factorThreshold * factorThreshold; // cancel ctx gestures if the distance b/t the fingers increases
25494
25495 if (factorSq >= factorThresholdSq || distance2Sq >= distThresholdSq) {
25496 r.touchData.cxt = false;
25497 r.data.bgActivePosistion = undefined;
25498 r.redrawHint('select', true);
25499 var cxtEvt = {
25500 originalEvent: e,
25501 type: 'cxttapend',
25502 position: {
25503 x: now[0],
25504 y: now[1]
25505 }
25506 };
25507
25508 if (r.touchData.start) {
25509 r.touchData.start.unactivate().emit(cxtEvt);
25510 r.touchData.start = null;
25511 } else {
25512 cy.emit(cxtEvt);
25513 }
25514 }
25515 } // context swipe
25516
25517
25518 if (capture && r.touchData.cxt) {
25519 var cxtEvt = {
25520 originalEvent: e,
25521 type: 'cxtdrag',
25522 position: {
25523 x: now[0],
25524 y: now[1]
25525 }
25526 };
25527 r.data.bgActivePosistion = undefined;
25528 r.redrawHint('select', true);
25529
25530 if (r.touchData.start) {
25531 r.touchData.start.emit(cxtEvt);
25532 } else {
25533 cy.emit(cxtEvt);
25534 }
25535
25536 if (r.touchData.start) {
25537 r.touchData.start._private.grabbed = false;
25538 }
25539
25540 r.touchData.cxtDragged = true;
25541 var near = r.findNearestElement(now[0], now[1], true, true);
25542
25543 if (!r.touchData.cxtOver || near !== r.touchData.cxtOver) {
25544 if (r.touchData.cxtOver) {
25545 r.touchData.cxtOver.emit({
25546 originalEvent: e,
25547 type: 'cxtdragout',
25548 position: {
25549 x: now[0],
25550 y: now[1]
25551 }
25552 });
25553 }
25554
25555 r.touchData.cxtOver = near;
25556
25557 if (near) {
25558 near.emit({
25559 originalEvent: e,
25560 type: 'cxtdragover',
25561 position: {
25562 x: now[0],
25563 y: now[1]
25564 }
25565 });
25566 }
25567 } // box selection
25568
25569 } else if (capture && e.touches[2] && cy.boxSelectionEnabled()) {
25570 e.preventDefault();
25571 r.data.bgActivePosistion = undefined;
25572 this.lastThreeTouch = +new Date();
25573
25574 if (!r.touchData.selecting) {
25575 cy.emit({
25576 originalEvent: e,
25577 type: 'boxstart',
25578 position: {
25579 x: now[0],
25580 y: now[1]
25581 }
25582 });
25583 }
25584
25585 r.touchData.selecting = true;
25586 r.touchData.didSelect = true;
25587 select[4] = 1;
25588
25589 if (!select || select.length === 0 || select[0] === undefined) {
25590 select[0] = (now[0] + now[2] + now[4]) / 3;
25591 select[1] = (now[1] + now[3] + now[5]) / 3;
25592 select[2] = (now[0] + now[2] + now[4]) / 3 + 1;
25593 select[3] = (now[1] + now[3] + now[5]) / 3 + 1;
25594 } else {
25595 select[2] = (now[0] + now[2] + now[4]) / 3;
25596 select[3] = (now[1] + now[3] + now[5]) / 3;
25597 }
25598
25599 r.redrawHint('select', true);
25600 r.redraw(); // pinch to zoom
25601 } else if (capture && e.touches[1] && !r.touchData.didSelect // don't allow box selection to degrade to pinch-to-zoom
25602 && cy.zoomingEnabled() && cy.panningEnabled() && cy.userZoomingEnabled() && cy.userPanningEnabled()) {
25603 // two fingers => pinch to zoom
25604 e.preventDefault();
25605 r.data.bgActivePosistion = undefined;
25606 r.redrawHint('select', true);
25607 var draggedEles = r.dragData.touchDragEles;
25608
25609 if (draggedEles) {
25610 r.redrawHint('drag', true);
25611
25612 for (var i = 0; i < draggedEles.length; i++) {
25613 var de_p = draggedEles[i]._private;
25614 de_p.grabbed = false;
25615 de_p.rscratch.inDragLayer = false;
25616 }
25617 }
25618
25619 var _start = r.touchData.start; // (x2, y2) for fingers 1 and 2
25620
25621 var f1x2 = e.touches[0].clientX - offsetLeft,
25622 f1y2 = e.touches[0].clientY - offsetTop;
25623 var f2x2 = e.touches[1].clientX - offsetLeft,
25624 f2y2 = e.touches[1].clientY - offsetTop;
25625 var distance2 = distance(f1x2, f1y2, f2x2, f2y2); // var distance2Sq = distanceSq( f1x2, f1y2, f2x2, f2y2 );
25626 // var factor = Math.sqrt( distance2Sq ) / Math.sqrt( distance1Sq );
25627
25628 var factor = distance2 / distance1;
25629
25630 if (twoFingersStartInside) {
25631 // delta finger1
25632 var df1x = f1x2 - f1x1;
25633 var df1y = f1y2 - f1y1; // delta finger 2
25634
25635 var df2x = f2x2 - f2x1;
25636 var df2y = f2y2 - f2y1; // translation is the normalised vector of the two fingers movement
25637 // i.e. so pinching cancels out and moving together pans
25638
25639 var tx = (df1x + df2x) / 2;
25640 var ty = (df1y + df2y) / 2; // now calculate the zoom
25641
25642 var zoom1 = cy.zoom();
25643 var zoom2 = zoom1 * factor;
25644 var pan1 = cy.pan(); // the model center point converted to the current rendered pos
25645
25646 var ctrx = modelCenter1[0] * zoom1 + pan1.x;
25647 var ctry = modelCenter1[1] * zoom1 + pan1.y;
25648 var pan2 = {
25649 x: -zoom2 / zoom1 * (ctrx - pan1.x - tx) + ctrx,
25650 y: -zoom2 / zoom1 * (ctry - pan1.y - ty) + ctry
25651 }; // remove dragged eles
25652
25653 if (_start && _start.active()) {
25654 var draggedEles = r.dragData.touchDragEles;
25655 freeDraggedElements(draggedEles);
25656 r.redrawHint('drag', true);
25657 r.redrawHint('eles', true);
25658
25659 _start.unactivate().emit('freeon');
25660
25661 draggedEles.emit('free');
25662
25663 if (r.dragData.didDrag) {
25664 _start.emit('dragfreeon');
25665
25666 draggedEles.emit('dragfree');
25667 }
25668 }
25669
25670 cy.viewport({
25671 zoom: zoom2,
25672 pan: pan2,
25673 cancelOnFailedZoom: true
25674 });
25675 cy.emit('pinchzoom');
25676 distance1 = distance2;
25677 f1x1 = f1x2;
25678 f1y1 = f1y2;
25679 f2x1 = f2x2;
25680 f2y1 = f2y2;
25681 r.pinching = true;
25682 } // Re-project
25683
25684
25685 if (e.touches[0]) {
25686 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25687 now[0] = pos[0];
25688 now[1] = pos[1];
25689 }
25690
25691 if (e.touches[1]) {
25692 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25693 now[2] = pos[0];
25694 now[3] = pos[1];
25695 }
25696
25697 if (e.touches[2]) {
25698 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25699 now[4] = pos[0];
25700 now[5] = pos[1];
25701 }
25702 } else if (e.touches[0] && !r.touchData.didSelect // don't allow box selection to degrade to single finger events like panning
25703 ) {
25704 var start = r.touchData.start;
25705 var last = r.touchData.last;
25706 var near;
25707
25708 if (!r.hoverData.draggingEles && !r.swipePanning) {
25709 near = r.findNearestElement(now[0], now[1], true, true);
25710 }
25711
25712 if (capture && start != null) {
25713 e.preventDefault();
25714 } // dragging nodes
25715
25716
25717 if (capture && start != null && r.nodeIsDraggable(start)) {
25718 if (isOverThresholdDrag) {
25719 // then dragging can happen
25720 var draggedEles = r.dragData.touchDragEles;
25721 var justStartedDrag = !r.dragData.didDrag;
25722
25723 if (justStartedDrag) {
25724 addNodesToDrag(draggedEles, {
25725 inDragLayer: true
25726 });
25727 }
25728
25729 r.dragData.didDrag = true;
25730 var totalShift = {
25731 x: 0,
25732 y: 0
25733 };
25734
25735 if (number(disp[0]) && number(disp[1])) {
25736 totalShift.x += disp[0];
25737 totalShift.y += disp[1];
25738
25739 if (justStartedDrag) {
25740 r.redrawHint('eles', true);
25741 var dragDelta = r.touchData.dragDelta;
25742
25743 if (dragDelta && number(dragDelta[0]) && number(dragDelta[1])) {
25744 totalShift.x += dragDelta[0];
25745 totalShift.y += dragDelta[1];
25746 }
25747 }
25748 }
25749
25750 r.hoverData.draggingEles = true;
25751 draggedEles.silentShift(totalShift).emit('position drag');
25752 r.redrawHint('drag', true);
25753
25754 if (r.touchData.startPosition[0] == earlier[0] && r.touchData.startPosition[1] == earlier[1]) {
25755 r.redrawHint('eles', true);
25756 }
25757
25758 r.redraw();
25759 } else {
25760 // otherise keep track of drag delta for later
25761 var dragDelta = r.touchData.dragDelta = r.touchData.dragDelta || [];
25762
25763 if (dragDelta.length === 0) {
25764 dragDelta.push(disp[0]);
25765 dragDelta.push(disp[1]);
25766 } else {
25767 dragDelta[0] += disp[0];
25768 dragDelta[1] += disp[1];
25769 }
25770 }
25771 } // touchmove
25772
25773
25774 {
25775 triggerEvents(start || near, ['touchmove', 'tapdrag', 'vmousemove'], e, {
25776 x: now[0],
25777 y: now[1]
25778 });
25779
25780 if ((!start || !start.grabbed()) && near != last) {
25781 if (last) {
25782 last.emit({
25783 originalEvent: e,
25784 type: 'tapdragout',
25785 position: {
25786 x: now[0],
25787 y: now[1]
25788 }
25789 });
25790 }
25791
25792 if (near) {
25793 near.emit({
25794 originalEvent: e,
25795 type: 'tapdragover',
25796 position: {
25797 x: now[0],
25798 y: now[1]
25799 }
25800 });
25801 }
25802 }
25803
25804 r.touchData.last = near;
25805 } // check to cancel taphold
25806
25807 if (capture) {
25808 for (var i = 0; i < now.length; i++) {
25809 if (now[i] && r.touchData.startPosition[i] && isOverThresholdDrag) {
25810 r.touchData.singleTouchMoved = true;
25811 }
25812 }
25813 } // panning
25814
25815
25816 if (capture && (start == null || start.pannable()) && cy.panningEnabled() && cy.userPanningEnabled()) {
25817 var allowPassthrough = allowPanningPassthrough(start, r.touchData.starts);
25818
25819 if (allowPassthrough) {
25820 e.preventDefault();
25821
25822 if (!r.data.bgActivePosistion) {
25823 r.data.bgActivePosistion = array2point(r.touchData.startPosition);
25824 }
25825
25826 if (r.swipePanning) {
25827 cy.panBy({
25828 x: disp[0] * zoom,
25829 y: disp[1] * zoom
25830 });
25831 cy.emit('dragpan');
25832 } else if (isOverThresholdDrag) {
25833 r.swipePanning = true;
25834 cy.panBy({
25835 x: dx * zoom,
25836 y: dy * zoom
25837 });
25838 cy.emit('dragpan');
25839
25840 if (start) {
25841 start.unactivate();
25842 r.redrawHint('select', true);
25843 r.touchData.start = null;
25844 }
25845 }
25846 } // Re-project
25847
25848
25849 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25850 now[0] = pos[0];
25851 now[1] = pos[1];
25852 }
25853 }
25854
25855 for (var j = 0; j < now.length; j++) {
25856 earlier[j] = now[j];
25857 } // the active bg indicator should be removed when making a swipe that is neither for dragging nodes or panning
25858
25859
25860 if (capture && e.touches.length > 0 && !r.hoverData.draggingEles && !r.swipePanning && r.data.bgActivePosistion != null) {
25861 r.data.bgActivePosistion = undefined;
25862 r.redrawHint('select', true);
25863 r.redraw();
25864 }
25865 }, false);
25866 var touchcancelHandler;
25867 r.registerBinding(window, 'touchcancel', touchcancelHandler = function touchcancelHandler(e) {
25868 // eslint-disable-line no-unused-vars
25869 var start = r.touchData.start;
25870 r.touchData.capture = false;
25871
25872 if (start) {
25873 start.unactivate();
25874 }
25875 });
25876 var touchendHandler;
25877 r.registerBinding(window, 'touchend', touchendHandler = function touchendHandler(e) {
25878 // eslint-disable-line no-unused-vars
25879 var start = r.touchData.start;
25880 var capture = r.touchData.capture;
25881
25882 if (capture) {
25883 if (e.touches.length === 0) {
25884 r.touchData.capture = false;
25885 }
25886
25887 e.preventDefault();
25888 } else {
25889 return;
25890 }
25891
25892 var select = r.selection;
25893 r.swipePanning = false;
25894 r.hoverData.draggingEles = false;
25895 var cy = r.cy;
25896 var zoom = cy.zoom();
25897 var now = r.touchData.now;
25898 var earlier = r.touchData.earlier;
25899
25900 if (e.touches[0]) {
25901 var pos = r.projectIntoViewport(e.touches[0].clientX, e.touches[0].clientY);
25902 now[0] = pos[0];
25903 now[1] = pos[1];
25904 }
25905
25906 if (e.touches[1]) {
25907 var pos = r.projectIntoViewport(e.touches[1].clientX, e.touches[1].clientY);
25908 now[2] = pos[0];
25909 now[3] = pos[1];
25910 }
25911
25912 if (e.touches[2]) {
25913 var pos = r.projectIntoViewport(e.touches[2].clientX, e.touches[2].clientY);
25914 now[4] = pos[0];
25915 now[5] = pos[1];
25916 }
25917
25918 if (start) {
25919 start.unactivate();
25920 }
25921
25922 var ctxTapend;
25923
25924 if (r.touchData.cxt) {
25925 ctxTapend = {
25926 originalEvent: e,
25927 type: 'cxttapend',
25928 position: {
25929 x: now[0],
25930 y: now[1]
25931 }
25932 };
25933
25934 if (start) {
25935 start.emit(ctxTapend);
25936 } else {
25937 cy.emit(ctxTapend);
25938 }
25939
25940 if (!r.touchData.cxtDragged) {
25941 var ctxTap = {
25942 originalEvent: e,
25943 type: 'cxttap',
25944 position: {
25945 x: now[0],
25946 y: now[1]
25947 }
25948 };
25949
25950 if (start) {
25951 start.emit(ctxTap);
25952 } else {
25953 cy.emit(ctxTap);
25954 }
25955 }
25956
25957 if (r.touchData.start) {
25958 r.touchData.start._private.grabbed = false;
25959 }
25960
25961 r.touchData.cxt = false;
25962 r.touchData.start = null;
25963 r.redraw();
25964 return;
25965 } // no more box selection if we don't have three fingers
25966
25967
25968 if (!e.touches[2] && cy.boxSelectionEnabled() && r.touchData.selecting) {
25969 r.touchData.selecting = false;
25970 var box = cy.collection(r.getAllInBox(select[0], select[1], select[2], select[3]));
25971 select[0] = undefined;
25972 select[1] = undefined;
25973 select[2] = undefined;
25974 select[3] = undefined;
25975 select[4] = 0;
25976 r.redrawHint('select', true);
25977 cy.emit({
25978 type: 'boxend',
25979 originalEvent: e,
25980 position: {
25981 x: now[0],
25982 y: now[1]
25983 }
25984 });
25985
25986 var eleWouldBeSelected = function eleWouldBeSelected(ele) {
25987 return ele.selectable() && !ele.selected();
25988 };
25989
25990 box.emit('box').stdFilter(eleWouldBeSelected).select().emit('boxselect');
25991
25992 if (box.nonempty()) {
25993 r.redrawHint('eles', true);
25994 }
25995
25996 r.redraw();
25997 }
25998
25999 if (start != null) {
26000 start.unactivate();
26001 }
26002
26003 if (e.touches[2]) {
26004 r.data.bgActivePosistion = undefined;
26005 r.redrawHint('select', true);
26006 } else if (e.touches[1]) ; else if (e.touches[0]) ; else if (!e.touches[0]) {
26007 r.data.bgActivePosistion = undefined;
26008 r.redrawHint('select', true);
26009 var draggedEles = r.dragData.touchDragEles;
26010
26011 if (start != null) {
26012 var startWasGrabbed = start._private.grabbed;
26013 freeDraggedElements(draggedEles);
26014 r.redrawHint('drag', true);
26015 r.redrawHint('eles', true);
26016
26017 if (startWasGrabbed) {
26018 start.emit('freeon');
26019 draggedEles.emit('free');
26020
26021 if (r.dragData.didDrag) {
26022 start.emit('dragfreeon');
26023 draggedEles.emit('dragfree');
26024 }
26025 }
26026
26027 triggerEvents(start, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26028 x: now[0],
26029 y: now[1]
26030 });
26031 start.unactivate();
26032 r.touchData.start = null;
26033 } else {
26034 var near = r.findNearestElement(now[0], now[1], true, true);
26035 triggerEvents(near, ['touchend', 'tapend', 'vmouseup', 'tapdragout'], e, {
26036 x: now[0],
26037 y: now[1]
26038 });
26039 }
26040
26041 var dx = r.touchData.startPosition[0] - now[0];
26042 var dx2 = dx * dx;
26043 var dy = r.touchData.startPosition[1] - now[1];
26044 var dy2 = dy * dy;
26045 var dist2 = dx2 + dy2;
26046 var rdist2 = dist2 * zoom * zoom; // Tap event, roughly same as mouse click event for touch
26047
26048 if (!r.touchData.singleTouchMoved) {
26049 if (!start) {
26050 cy.$(':selected').unselect(['tapunselect']);
26051 }
26052
26053 triggerEvents(start, ['tap', 'vclick'], e, {
26054 x: now[0],
26055 y: now[1]
26056 });
26057 } // Prepare to select the currently touched node, only if it hasn't been dragged past a certain distance
26058
26059
26060 if (start != null && !r.dragData.didDrag // didn't drag nodes around
26061 && start._private.selectable && rdist2 < r.touchTapThreshold2 && !r.pinching // pinch to zoom should not affect selection
26062 ) {
26063 if (cy.selectionType() === 'single') {
26064 cy.$(isSelected).unmerge(start).unselect(['tapunselect']);
26065 start.select(['tapselect']);
26066 } else {
26067 if (start.selected()) {
26068 start.unselect(['tapunselect']);
26069 } else {
26070 start.select(['tapselect']);
26071 }
26072 }
26073
26074 r.redrawHint('eles', true);
26075 }
26076
26077 r.touchData.singleTouchMoved = true;
26078 }
26079
26080 for (var j = 0; j < now.length; j++) {
26081 earlier[j] = now[j];
26082 }
26083
26084 r.dragData.didDrag = false; // reset for next touchstart
26085
26086 if (e.touches.length === 0) {
26087 r.touchData.dragDelta = [];
26088 r.touchData.startPosition = null;
26089 r.touchData.startGPosition = null;
26090 r.touchData.didSelect = false;
26091 }
26092
26093 if (e.touches.length < 2) {
26094 if (e.touches.length === 1) {
26095 // the old start global pos'n may not be the same finger that remains
26096 r.touchData.startGPosition = [e.touches[0].clientX, e.touches[0].clientY];
26097 }
26098
26099 r.pinching = false;
26100 r.redrawHint('eles', true);
26101 r.redraw();
26102 } //r.redraw();
26103
26104 }, false); // fallback compatibility layer for ms pointer events
26105
26106 if (typeof TouchEvent === 'undefined') {
26107 var pointers = [];
26108
26109 var makeTouch = function makeTouch(e) {
26110 return {
26111 clientX: e.clientX,
26112 clientY: e.clientY,
26113 force: 1,
26114 identifier: e.pointerId,
26115 pageX: e.pageX,
26116 pageY: e.pageY,
26117 radiusX: e.width / 2,
26118 radiusY: e.height / 2,
26119 screenX: e.screenX,
26120 screenY: e.screenY,
26121 target: e.target
26122 };
26123 };
26124
26125 var makePointer = function makePointer(e) {
26126 return {
26127 event: e,
26128 touch: makeTouch(e)
26129 };
26130 };
26131
26132 var addPointer = function addPointer(e) {
26133 pointers.push(makePointer(e));
26134 };
26135
26136 var removePointer = function removePointer(e) {
26137 for (var i = 0; i < pointers.length; i++) {
26138 var p = pointers[i];
26139
26140 if (p.event.pointerId === e.pointerId) {
26141 pointers.splice(i, 1);
26142 return;
26143 }
26144 }
26145 };
26146
26147 var updatePointer = function updatePointer(e) {
26148 var p = pointers.filter(function (p) {
26149 return p.event.pointerId === e.pointerId;
26150 })[0];
26151 p.event = e;
26152 p.touch = makeTouch(e);
26153 };
26154
26155 var addTouchesToEvent = function addTouchesToEvent(e) {
26156 e.touches = pointers.map(function (p) {
26157 return p.touch;
26158 });
26159 };
26160
26161 var pointerIsMouse = function pointerIsMouse(e) {
26162 return e.pointerType === 'mouse' || e.pointerType === 4;
26163 };
26164
26165 r.registerBinding(r.container, 'pointerdown', function (e) {
26166 if (pointerIsMouse(e)) {
26167 return;
26168 } // mouse already handled
26169
26170
26171 e.preventDefault();
26172 addPointer(e);
26173 addTouchesToEvent(e);
26174 touchstartHandler(e);
26175 });
26176 r.registerBinding(r.container, 'pointerup', function (e) {
26177 if (pointerIsMouse(e)) {
26178 return;
26179 } // mouse already handled
26180
26181
26182 removePointer(e);
26183 addTouchesToEvent(e);
26184 touchendHandler(e);
26185 });
26186 r.registerBinding(r.container, 'pointercancel', function (e) {
26187 if (pointerIsMouse(e)) {
26188 return;
26189 } // mouse already handled
26190
26191
26192 removePointer(e);
26193 addTouchesToEvent(e);
26194 touchcancelHandler(e);
26195 });
26196 r.registerBinding(r.container, 'pointermove', function (e) {
26197 if (pointerIsMouse(e)) {
26198 return;
26199 } // mouse already handled
26200
26201
26202 e.preventDefault();
26203 updatePointer(e);
26204 addTouchesToEvent(e);
26205 touchmoveHandler(e);
26206 });
26207 }
26208};
26209
26210var BRp$d = {};
26211
26212BRp$d.generatePolygon = function (name, points) {
26213 return this.nodeShapes[name] = {
26214 renderer: this,
26215 name: name,
26216 points: points,
26217 draw: function draw(context, centerX, centerY, width, height) {
26218 this.renderer.nodeShapeImpl('polygon', context, centerX, centerY, width, height, this.points);
26219 },
26220 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26221 return polygonIntersectLine(x, y, this.points, nodeX, nodeY, width / 2, height / 2, padding);
26222 },
26223 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26224 return pointInsidePolygon(x, y, this.points, centerX, centerY, width, height, [0, -1], padding);
26225 }
26226 };
26227};
26228
26229BRp$d.generateEllipse = function () {
26230 return this.nodeShapes['ellipse'] = {
26231 renderer: this,
26232 name: 'ellipse',
26233 draw: function draw(context, centerX, centerY, width, height) {
26234 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26235 },
26236 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26237 return intersectLineEllipse(x, y, nodeX, nodeY, width / 2 + padding, height / 2 + padding);
26238 },
26239 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26240 return checkInEllipse(x, y, width, height, centerX, centerY, padding);
26241 }
26242 };
26243};
26244
26245BRp$d.generateRoundPolygon = function (name, points) {
26246 // Pre-compute control points
26247 // Since these points depend on the radius length (which in turns depend on the width/height of the node) we will only pre-compute
26248 // the unit vectors.
26249 // For simplicity the layout will be:
26250 // [ p0, UnitVectorP0P1, p1, UniVectorP1P2, ..., pn, UnitVectorPnP0 ]
26251 var allPoints = new Array(points.length * 2);
26252
26253 for (var i = 0; i < points.length / 2; i++) {
26254 var sourceIndex = i * 2;
26255 var destIndex = void 0;
26256
26257 if (i < points.length / 2 - 1) {
26258 destIndex = (i + 1) * 2;
26259 } else {
26260 destIndex = 0;
26261 }
26262
26263 allPoints[i * 4] = points[sourceIndex];
26264 allPoints[i * 4 + 1] = points[sourceIndex + 1];
26265 var xDest = points[destIndex] - points[sourceIndex];
26266 var yDest = points[destIndex + 1] - points[sourceIndex + 1];
26267 var norm = Math.sqrt(xDest * xDest + yDest * yDest);
26268 allPoints[i * 4 + 2] = xDest / norm;
26269 allPoints[i * 4 + 3] = yDest / norm;
26270 }
26271
26272 return this.nodeShapes[name] = {
26273 renderer: this,
26274 name: name,
26275 points: allPoints,
26276 draw: function draw(context, centerX, centerY, width, height) {
26277 this.renderer.nodeShapeImpl('round-polygon', context, centerX, centerY, width, height, this.points);
26278 },
26279 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26280 return roundPolygonIntersectLine(x, y, this.points, nodeX, nodeY, width, height);
26281 },
26282 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26283 return pointInsideRoundPolygon(x, y, this.points, centerX, centerY, width, height);
26284 }
26285 };
26286};
26287
26288BRp$d.generateRoundRectangle = function () {
26289 return this.nodeShapes['round-rectangle'] = this.nodeShapes['roundrectangle'] = {
26290 renderer: this,
26291 name: 'round-rectangle',
26292 points: generateUnitNgonPointsFitToSquare(4, 0),
26293 draw: function draw(context, centerX, centerY, width, height) {
26294 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26295 },
26296 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26297 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26298 },
26299 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26300 var cornerRadius = getRoundRectangleRadius(width, height);
26301 var diam = cornerRadius * 2; // Check hBox
26302
26303 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26304 return true;
26305 } // Check vBox
26306
26307
26308 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26309 return true;
26310 } // Check top left quarter circle
26311
26312
26313 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26314 return true;
26315 } // Check top right quarter circle
26316
26317
26318 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY - height / 2 + cornerRadius, padding)) {
26319 return true;
26320 } // Check bottom right quarter circle
26321
26322
26323 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26324 return true;
26325 } // Check bottom left quarter circle
26326
26327
26328 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26329 return true;
26330 }
26331
26332 return false;
26333 }
26334 };
26335};
26336
26337BRp$d.generateCutRectangle = function () {
26338 return this.nodeShapes['cut-rectangle'] = this.nodeShapes['cutrectangle'] = {
26339 renderer: this,
26340 name: 'cut-rectangle',
26341 cornerLength: getCutRectangleCornerLength(),
26342 points: generateUnitNgonPointsFitToSquare(4, 0),
26343 draw: function draw(context, centerX, centerY, width, height) {
26344 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26345 },
26346 generateCutTrianglePts: function generateCutTrianglePts(width, height, centerX, centerY) {
26347 var cl = this.cornerLength;
26348 var hh = height / 2;
26349 var hw = width / 2;
26350 var xBegin = centerX - hw;
26351 var xEnd = centerX + hw;
26352 var yBegin = centerY - hh;
26353 var yEnd = centerY + hh; // points are in clockwise order, inner (imaginary) triangle pt on [4, 5]
26354
26355 return {
26356 topLeft: [xBegin, yBegin + cl, xBegin + cl, yBegin, xBegin + cl, yBegin + cl],
26357 topRight: [xEnd - cl, yBegin, xEnd, yBegin + cl, xEnd - cl, yBegin + cl],
26358 bottomRight: [xEnd, yEnd - cl, xEnd - cl, yEnd, xEnd - cl, yEnd - cl],
26359 bottomLeft: [xBegin + cl, yEnd, xBegin, yEnd - cl, xBegin + cl, yEnd - cl]
26360 };
26361 },
26362 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26363 var cPts = this.generateCutTrianglePts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26364 var pts = [].concat.apply([], [cPts.topLeft.splice(0, 4), cPts.topRight.splice(0, 4), cPts.bottomRight.splice(0, 4), cPts.bottomLeft.splice(0, 4)]);
26365 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26366 },
26367 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26368 // Check hBox
26369 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * this.cornerLength, [0, -1], padding)) {
26370 return true;
26371 } // Check vBox
26372
26373
26374 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * this.cornerLength, height, [0, -1], padding)) {
26375 return true;
26376 }
26377
26378 var cutTrianglePts = this.generateCutTrianglePts(width, height, centerX, centerY);
26379 return pointInsidePolygonPoints(x, y, cutTrianglePts.topLeft) || pointInsidePolygonPoints(x, y, cutTrianglePts.topRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomRight) || pointInsidePolygonPoints(x, y, cutTrianglePts.bottomLeft);
26380 }
26381 };
26382};
26383
26384BRp$d.generateBarrel = function () {
26385 return this.nodeShapes['barrel'] = {
26386 renderer: this,
26387 name: 'barrel',
26388 points: generateUnitNgonPointsFitToSquare(4, 0),
26389 draw: function draw(context, centerX, centerY, width, height) {
26390 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26391 },
26392 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26393 // use two fixed t values for the bezier curve approximation
26394 var t0 = 0.15;
26395 var t1 = 0.5;
26396 var t2 = 0.85;
26397 var bPts = this.generateBarrelBezierPts(width + 2 * padding, height + 2 * padding, nodeX, nodeY);
26398
26399 var approximateBarrelCurvePts = function approximateBarrelCurvePts(pts) {
26400 // approximate curve pts based on the two t values
26401 var m0 = qbezierPtAt({
26402 x: pts[0],
26403 y: pts[1]
26404 }, {
26405 x: pts[2],
26406 y: pts[3]
26407 }, {
26408 x: pts[4],
26409 y: pts[5]
26410 }, t0);
26411 var m1 = qbezierPtAt({
26412 x: pts[0],
26413 y: pts[1]
26414 }, {
26415 x: pts[2],
26416 y: pts[3]
26417 }, {
26418 x: pts[4],
26419 y: pts[5]
26420 }, t1);
26421 var m2 = qbezierPtAt({
26422 x: pts[0],
26423 y: pts[1]
26424 }, {
26425 x: pts[2],
26426 y: pts[3]
26427 }, {
26428 x: pts[4],
26429 y: pts[5]
26430 }, t2);
26431 return [pts[0], pts[1], m0.x, m0.y, m1.x, m1.y, m2.x, m2.y, pts[4], pts[5]];
26432 };
26433
26434 var pts = [].concat(approximateBarrelCurvePts(bPts.topLeft), approximateBarrelCurvePts(bPts.topRight), approximateBarrelCurvePts(bPts.bottomRight), approximateBarrelCurvePts(bPts.bottomLeft));
26435 return polygonIntersectLine(x, y, pts, nodeX, nodeY);
26436 },
26437 generateBarrelBezierPts: function generateBarrelBezierPts(width, height, centerX, centerY) {
26438 var hh = height / 2;
26439 var hw = width / 2;
26440 var xBegin = centerX - hw;
26441 var xEnd = centerX + hw;
26442 var yBegin = centerY - hh;
26443 var yEnd = centerY + hh;
26444 var curveConstants = getBarrelCurveConstants(width, height);
26445 var hOffset = curveConstants.heightOffset;
26446 var wOffset = curveConstants.widthOffset;
26447 var ctrlPtXOffset = curveConstants.ctrlPtOffsetPct * width; // points are in clockwise order, inner (imaginary) control pt on [4, 5]
26448
26449 var pts = {
26450 topLeft: [xBegin, yBegin + hOffset, xBegin + ctrlPtXOffset, yBegin, xBegin + wOffset, yBegin],
26451 topRight: [xEnd - wOffset, yBegin, xEnd - ctrlPtXOffset, yBegin, xEnd, yBegin + hOffset],
26452 bottomRight: [xEnd, yEnd - hOffset, xEnd - ctrlPtXOffset, yEnd, xEnd - wOffset, yEnd],
26453 bottomLeft: [xBegin + wOffset, yEnd, xBegin + ctrlPtXOffset, yEnd, xBegin, yEnd - hOffset]
26454 };
26455 pts.topLeft.isTop = true;
26456 pts.topRight.isTop = true;
26457 pts.bottomLeft.isBottom = true;
26458 pts.bottomRight.isBottom = true;
26459 return pts;
26460 },
26461 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26462 var curveConstants = getBarrelCurveConstants(width, height);
26463 var hOffset = curveConstants.heightOffset;
26464 var wOffset = curveConstants.widthOffset; // Check hBox
26465
26466 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - 2 * hOffset, [0, -1], padding)) {
26467 return true;
26468 } // Check vBox
26469
26470
26471 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - 2 * wOffset, height, [0, -1], padding)) {
26472 return true;
26473 }
26474
26475 var barrelCurvePts = this.generateBarrelBezierPts(width, height, centerX, centerY);
26476
26477 var getCurveT = function getCurveT(x, y, curvePts) {
26478 var x0 = curvePts[4];
26479 var x1 = curvePts[2];
26480 var x2 = curvePts[0];
26481 var y0 = curvePts[5]; // var y1 = curvePts[ 3 ];
26482
26483 var y2 = curvePts[1];
26484 var xMin = Math.min(x0, x2);
26485 var xMax = Math.max(x0, x2);
26486 var yMin = Math.min(y0, y2);
26487 var yMax = Math.max(y0, y2);
26488
26489 if (xMin <= x && x <= xMax && yMin <= y && y <= yMax) {
26490 var coeff = bezierPtsToQuadCoeff(x0, x1, x2);
26491 var roots = solveQuadratic(coeff[0], coeff[1], coeff[2], x);
26492 var validRoots = roots.filter(function (r) {
26493 return 0 <= r && r <= 1;
26494 });
26495
26496 if (validRoots.length > 0) {
26497 return validRoots[0];
26498 }
26499 }
26500
26501 return null;
26502 };
26503
26504 var curveRegions = Object.keys(barrelCurvePts);
26505
26506 for (var i = 0; i < curveRegions.length; i++) {
26507 var corner = curveRegions[i];
26508 var cornerPts = barrelCurvePts[corner];
26509 var t = getCurveT(x, y, cornerPts);
26510
26511 if (t == null) {
26512 continue;
26513 }
26514
26515 var y0 = cornerPts[5];
26516 var y1 = cornerPts[3];
26517 var y2 = cornerPts[1];
26518 var bezY = qbezierAt(y0, y1, y2, t);
26519
26520 if (cornerPts.isTop && bezY <= y) {
26521 return true;
26522 }
26523
26524 if (cornerPts.isBottom && y <= bezY) {
26525 return true;
26526 }
26527 }
26528
26529 return false;
26530 }
26531 };
26532};
26533
26534BRp$d.generateBottomRoundrectangle = function () {
26535 return this.nodeShapes['bottom-round-rectangle'] = this.nodeShapes['bottomroundrectangle'] = {
26536 renderer: this,
26537 name: 'bottom-round-rectangle',
26538 points: generateUnitNgonPointsFitToSquare(4, 0),
26539 draw: function draw(context, centerX, centerY, width, height) {
26540 this.renderer.nodeShapeImpl(this.name, context, centerX, centerY, width, height);
26541 },
26542 intersectLine: function intersectLine(nodeX, nodeY, width, height, x, y, padding) {
26543 var topStartX = nodeX - (width / 2 + padding);
26544 var topStartY = nodeY - (height / 2 + padding);
26545 var topEndY = topStartY;
26546 var topEndX = nodeX + (width / 2 + padding);
26547 var topIntersections = finiteLinesIntersect(x, y, nodeX, nodeY, topStartX, topStartY, topEndX, topEndY, false);
26548
26549 if (topIntersections.length > 0) {
26550 return topIntersections;
26551 }
26552
26553 return roundRectangleIntersectLine(x, y, nodeX, nodeY, width, height, padding);
26554 },
26555 checkPoint: function checkPoint(x, y, padding, width, height, centerX, centerY) {
26556 var cornerRadius = getRoundRectangleRadius(width, height);
26557 var diam = 2 * cornerRadius; // Check hBox
26558
26559 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width, height - diam, [0, -1], padding)) {
26560 return true;
26561 } // Check vBox
26562
26563
26564 if (pointInsidePolygon(x, y, this.points, centerX, centerY, width - diam, height, [0, -1], padding)) {
26565 return true;
26566 } // check non-rounded top side
26567
26568
26569 var outerWidth = width / 2 + 2 * padding;
26570 var outerHeight = height / 2 + 2 * padding;
26571 var points = [centerX - outerWidth, centerY - outerHeight, centerX - outerWidth, centerY, centerX + outerWidth, centerY, centerX + outerWidth, centerY - outerHeight];
26572
26573 if (pointInsidePolygonPoints(x, y, points)) {
26574 return true;
26575 } // Check bottom right quarter circle
26576
26577
26578 if (checkInEllipse(x, y, diam, diam, centerX + width / 2 - cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26579 return true;
26580 } // Check bottom left quarter circle
26581
26582
26583 if (checkInEllipse(x, y, diam, diam, centerX - width / 2 + cornerRadius, centerY + height / 2 - cornerRadius, padding)) {
26584 return true;
26585 }
26586
26587 return false;
26588 }
26589 };
26590};
26591
26592BRp$d.registerNodeShapes = function () {
26593 var nodeShapes = this.nodeShapes = {};
26594 var renderer = this;
26595 this.generateEllipse();
26596 this.generatePolygon('triangle', generateUnitNgonPointsFitToSquare(3, 0));
26597 this.generateRoundPolygon('round-triangle', generateUnitNgonPointsFitToSquare(3, 0));
26598 this.generatePolygon('rectangle', generateUnitNgonPointsFitToSquare(4, 0));
26599 nodeShapes['square'] = nodeShapes['rectangle'];
26600 this.generateRoundRectangle();
26601 this.generateCutRectangle();
26602 this.generateBarrel();
26603 this.generateBottomRoundrectangle();
26604 {
26605 var diamondPoints = [0, 1, 1, 0, 0, -1, -1, 0];
26606 this.generatePolygon('diamond', diamondPoints);
26607 this.generateRoundPolygon('round-diamond', diamondPoints);
26608 }
26609 this.generatePolygon('pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26610 this.generateRoundPolygon('round-pentagon', generateUnitNgonPointsFitToSquare(5, 0));
26611 this.generatePolygon('hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26612 this.generateRoundPolygon('round-hexagon', generateUnitNgonPointsFitToSquare(6, 0));
26613 this.generatePolygon('heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26614 this.generateRoundPolygon('round-heptagon', generateUnitNgonPointsFitToSquare(7, 0));
26615 this.generatePolygon('octagon', generateUnitNgonPointsFitToSquare(8, 0));
26616 this.generateRoundPolygon('round-octagon', generateUnitNgonPointsFitToSquare(8, 0));
26617 var star5Points = new Array(20);
26618 {
26619 var outerPoints = generateUnitNgonPoints(5, 0);
26620 var innerPoints = generateUnitNgonPoints(5, Math.PI / 5); // Outer radius is 1; inner radius of star is smaller
26621
26622 var innerRadius = 0.5 * (3 - Math.sqrt(5));
26623 innerRadius *= 1.57;
26624
26625 for (var i = 0; i < innerPoints.length / 2; i++) {
26626 innerPoints[i * 2] *= innerRadius;
26627 innerPoints[i * 2 + 1] *= innerRadius;
26628 }
26629
26630 for (var i = 0; i < 20 / 4; i++) {
26631 star5Points[i * 4] = outerPoints[i * 2];
26632 star5Points[i * 4 + 1] = outerPoints[i * 2 + 1];
26633 star5Points[i * 4 + 2] = innerPoints[i * 2];
26634 star5Points[i * 4 + 3] = innerPoints[i * 2 + 1];
26635 }
26636 }
26637 star5Points = fitPolygonToSquare(star5Points);
26638 this.generatePolygon('star', star5Points);
26639 this.generatePolygon('vee', [-1, -1, 0, -0.333, 1, -1, 0, 1]);
26640 this.generatePolygon('rhomboid', [-1, -1, 0.333, -1, 1, 1, -0.333, 1]);
26641 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]);
26642 {
26643 var tagPoints = [-1, -1, 0.25, -1, 1, 0, 0.25, 1, -1, 1];
26644 this.generatePolygon('tag', tagPoints);
26645 this.generateRoundPolygon('round-tag', tagPoints);
26646 }
26647
26648 nodeShapes.makePolygon = function (points) {
26649 // use caching on user-specified polygons so they are as fast as native shapes
26650 var key = points.join('$');
26651 var name = 'polygon-' + key;
26652 var shape;
26653
26654 if (shape = this[name]) {
26655 // got cached shape
26656 return shape;
26657 } // create and cache new shape
26658
26659
26660 return renderer.generatePolygon(name, points);
26661 };
26662};
26663
26664var BRp$e = {};
26665
26666BRp$e.timeToRender = function () {
26667 return this.redrawTotalTime / this.redrawCount;
26668};
26669
26670BRp$e.redraw = function (options) {
26671 options = options || staticEmptyObject();
26672 var r = this;
26673
26674 if (r.averageRedrawTime === undefined) {
26675 r.averageRedrawTime = 0;
26676 }
26677
26678 if (r.lastRedrawTime === undefined) {
26679 r.lastRedrawTime = 0;
26680 }
26681
26682 if (r.lastDrawTime === undefined) {
26683 r.lastDrawTime = 0;
26684 }
26685
26686 r.requestedFrame = true;
26687 r.renderOptions = options;
26688};
26689
26690BRp$e.beforeRender = function (fn, priority) {
26691 // the renderer can't add tick callbacks when destroyed
26692 if (this.destroyed) {
26693 return;
26694 }
26695
26696 if (priority == null) {
26697 error('Priority is not optional for beforeRender');
26698 }
26699
26700 var cbs = this.beforeRenderCallbacks;
26701 cbs.push({
26702 fn: fn,
26703 priority: priority
26704 }); // higher priority callbacks executed first
26705
26706 cbs.sort(function (a, b) {
26707 return b.priority - a.priority;
26708 });
26709};
26710
26711var beforeRenderCallbacks = function beforeRenderCallbacks(r, willDraw, startTime) {
26712 var cbs = r.beforeRenderCallbacks;
26713
26714 for (var i = 0; i < cbs.length; i++) {
26715 cbs[i].fn(willDraw, startTime);
26716 }
26717};
26718
26719BRp$e.startRenderLoop = function () {
26720 var r = this;
26721 var cy = r.cy;
26722
26723 if (r.renderLoopStarted) {
26724 return;
26725 } else {
26726 r.renderLoopStarted = true;
26727 }
26728
26729 var renderFn = function renderFn(requestTime) {
26730 if (r.destroyed) {
26731 return;
26732 }
26733
26734 if (cy.batching()) ; else if (r.requestedFrame && !r.skipFrame) {
26735 beforeRenderCallbacks(r, true, requestTime);
26736 var startTime = performanceNow();
26737 r.render(r.renderOptions);
26738 var endTime = r.lastDrawTime = performanceNow();
26739
26740 if (r.averageRedrawTime === undefined) {
26741 r.averageRedrawTime = endTime - startTime;
26742 }
26743
26744 if (r.redrawCount === undefined) {
26745 r.redrawCount = 0;
26746 }
26747
26748 r.redrawCount++;
26749
26750 if (r.redrawTotalTime === undefined) {
26751 r.redrawTotalTime = 0;
26752 }
26753
26754 var duration = endTime - startTime;
26755 r.redrawTotalTime += duration;
26756 r.lastRedrawTime = duration; // use a weighted average with a bias from the previous average so we don't spike so easily
26757
26758 r.averageRedrawTime = r.averageRedrawTime / 2 + duration / 2;
26759 r.requestedFrame = false;
26760 } else {
26761 beforeRenderCallbacks(r, false, requestTime);
26762 }
26763
26764 r.skipFrame = false;
26765 requestAnimationFrame(renderFn);
26766 };
26767
26768 requestAnimationFrame(renderFn);
26769};
26770
26771var BaseRenderer = function BaseRenderer(options) {
26772 this.init(options);
26773};
26774
26775var BR = BaseRenderer;
26776var BRp$f = BR.prototype;
26777BRp$f.clientFunctions = ['redrawHint', 'render', 'renderTo', 'matchCanvasSize', 'nodeShapeImpl', 'arrowShapeImpl'];
26778
26779BRp$f.init = function (options) {
26780 var r = this;
26781 r.options = options;
26782 r.cy = options.cy;
26783 var ctr = r.container = options.cy.container(); // prepend a stylesheet in the head such that
26784
26785 if (window$1) {
26786 var document = window$1.document;
26787 var head = document.head;
26788 var stylesheetId = '__________cytoscape_stylesheet';
26789 var className = '__________cytoscape_container';
26790 var stylesheetAlreadyExists = document.getElementById(stylesheetId) != null;
26791
26792 if (ctr.className.indexOf(className) < 0) {
26793 ctr.className = (ctr.className || '') + ' ' + className;
26794 }
26795
26796 if (!stylesheetAlreadyExists) {
26797 var stylesheet = document.createElement('style');
26798 stylesheet.id = stylesheetId;
26799 stylesheet.innerHTML = '.' + className + ' { position: relative; }';
26800 head.insertBefore(stylesheet, head.children[0]); // first so lowest priority
26801 }
26802
26803 var computedStyle = window$1.getComputedStyle(ctr);
26804 var position = computedStyle.getPropertyValue('position');
26805
26806 if (position === 'static') {
26807 warn('A Cytoscape container has style position:static and so can not use UI extensions properly');
26808 }
26809 }
26810
26811 r.selection = [undefined, undefined, undefined, undefined, 0]; // Coordinates for selection box, plus enabled flag
26812
26813 r.bezierProjPcts = [0.05, 0.225, 0.4, 0.5, 0.6, 0.775, 0.95]; //--Pointer-related data
26814
26815 r.hoverData = {
26816 down: null,
26817 last: null,
26818 downTime: null,
26819 triggerMode: null,
26820 dragging: false,
26821 initialPan: [null, null],
26822 capture: false
26823 };
26824 r.dragData = {
26825 possibleDragElements: []
26826 };
26827 r.touchData = {
26828 start: null,
26829 capture: false,
26830 // These 3 fields related to tap, taphold events
26831 startPosition: [null, null, null, null, null, null],
26832 singleTouchStartTime: null,
26833 singleTouchMoved: true,
26834 now: [null, null, null, null, null, null],
26835 earlier: [null, null, null, null, null, null]
26836 };
26837 r.redraws = 0;
26838 r.showFps = options.showFps;
26839 r.debug = options.debug;
26840 r.hideEdgesOnViewport = options.hideEdgesOnViewport;
26841 r.textureOnViewport = options.textureOnViewport;
26842 r.wheelSensitivity = options.wheelSensitivity;
26843 r.motionBlurEnabled = options.motionBlur; // on by default
26844
26845 r.forcedPixelRatio = number(options.pixelRatio) ? options.pixelRatio : null;
26846 r.motionBlur = options.motionBlur; // for initial kick off
26847
26848 r.motionBlurOpacity = options.motionBlurOpacity;
26849 r.motionBlurTransparency = 1 - r.motionBlurOpacity;
26850 r.motionBlurPxRatio = 1;
26851 r.mbPxRBlurry = 1; //0.8;
26852
26853 r.minMbLowQualFrames = 4;
26854 r.fullQualityMb = false;
26855 r.clearedForMotionBlur = [];
26856 r.desktopTapThreshold = options.desktopTapThreshold;
26857 r.desktopTapThreshold2 = options.desktopTapThreshold * options.desktopTapThreshold;
26858 r.touchTapThreshold = options.touchTapThreshold;
26859 r.touchTapThreshold2 = options.touchTapThreshold * options.touchTapThreshold;
26860 r.tapholdDuration = 500;
26861 r.bindings = [];
26862 r.beforeRenderCallbacks = [];
26863 r.beforeRenderPriorities = {
26864 // higher priority execs before lower one
26865 animations: 400,
26866 eleCalcs: 300,
26867 eleTxrDeq: 200,
26868 lyrTxrDeq: 150,
26869 lyrTxrSkip: 100
26870 };
26871 r.registerNodeShapes();
26872 r.registerArrowShapes();
26873 r.registerCalculationListeners();
26874};
26875
26876BRp$f.notify = function (eventName, eles) {
26877 var r = this;
26878 var cy = r.cy; // the renderer can't be notified after it's destroyed
26879
26880 if (this.destroyed) {
26881 return;
26882 }
26883
26884 if (eventName === 'init') {
26885 r.load();
26886 return;
26887 }
26888
26889 if (eventName === 'destroy') {
26890 r.destroy();
26891 return;
26892 }
26893
26894 if (eventName === 'add' || eventName === 'remove' || eventName === 'move' && cy.hasCompoundNodes() || eventName === 'load' || eventName === 'zorder' || eventName === 'mount') {
26895 r.invalidateCachedZSortedEles();
26896 }
26897
26898 if (eventName === 'viewport') {
26899 r.redrawHint('select', true);
26900 }
26901
26902 if (eventName === 'load' || eventName === 'resize' || eventName === 'mount') {
26903 r.invalidateContainerClientCoordsCache();
26904 r.matchCanvasSize(r.container);
26905 }
26906
26907 r.redrawHint('eles', true);
26908 r.redrawHint('drag', true);
26909 this.startRenderLoop();
26910 this.redraw();
26911};
26912
26913BRp$f.destroy = function () {
26914 var r = this;
26915 r.destroyed = true;
26916 r.cy.stopAnimationLoop();
26917
26918 for (var i = 0; i < r.bindings.length; i++) {
26919 var binding = r.bindings[i];
26920 var b = binding;
26921 var tgt = b.target;
26922 (tgt.off || tgt.removeEventListener).apply(tgt, b.args);
26923 }
26924
26925 r.bindings = [];
26926 r.beforeRenderCallbacks = [];
26927 r.onUpdateEleCalcsFns = [];
26928
26929 if (r.removeObserver) {
26930 r.removeObserver.disconnect();
26931 }
26932
26933 if (r.styleObserver) {
26934 r.styleObserver.disconnect();
26935 }
26936
26937 if (r.resizeObserver) {
26938 r.resizeObserver.disconnect();
26939 }
26940
26941 if (r.labelCalcDiv) {
26942 try {
26943 document.body.removeChild(r.labelCalcDiv); // eslint-disable-line no-undef
26944 } catch (e) {// ie10 issue #1014
26945 }
26946 }
26947};
26948
26949BRp$f.isHeadless = function () {
26950 return false;
26951};
26952
26953[BRp, BRp$a, BRp$b, BRp$c, BRp$d, BRp$e].forEach(function (props) {
26954 extend(BRp$f, props);
26955});
26956
26957var fullFpsTime = 1000 / 60; // assume 60 frames per second
26958
26959var defs = {
26960 setupDequeueing: function setupDequeueing(opts) {
26961 return function setupDequeueingImpl() {
26962 var self = this;
26963 var r = this.renderer;
26964
26965 if (self.dequeueingSetup) {
26966 return;
26967 } else {
26968 self.dequeueingSetup = true;
26969 }
26970
26971 var queueRedraw = util(function () {
26972 r.redrawHint('eles', true);
26973 r.redrawHint('drag', true);
26974 r.redraw();
26975 }, opts.deqRedrawThreshold);
26976
26977 var dequeue = function dequeue(willDraw, frameStartTime) {
26978 var startTime = performanceNow();
26979 var avgRenderTime = r.averageRedrawTime;
26980 var renderTime = r.lastRedrawTime;
26981 var deqd = [];
26982 var extent = r.cy.extent();
26983 var pixelRatio = r.getPixelRatio(); // if we aren't in a tick that causes a draw, then the rendered style
26984 // queue won't automatically be flushed before dequeueing starts
26985
26986 if (!willDraw) {
26987 r.flushRenderedStyleQueue();
26988 }
26989
26990 while (true) {
26991 // eslint-disable-line no-constant-condition
26992 var now = performanceNow();
26993 var duration = now - startTime;
26994 var frameDuration = now - frameStartTime;
26995
26996 if (renderTime < fullFpsTime) {
26997 // if we're rendering faster than the ideal fps, then do dequeueing
26998 // during all of the remaining frame time
26999 var timeAvailable = fullFpsTime - (willDraw ? avgRenderTime : 0);
27000
27001 if (frameDuration >= opts.deqFastCost * timeAvailable) {
27002 break;
27003 }
27004 } else {
27005 if (willDraw) {
27006 if (duration >= opts.deqCost * renderTime || duration >= opts.deqAvgCost * avgRenderTime) {
27007 break;
27008 }
27009 } else if (frameDuration >= opts.deqNoDrawCost * fullFpsTime) {
27010 break;
27011 }
27012 }
27013
27014 var thisDeqd = opts.deq(self, pixelRatio, extent);
27015
27016 if (thisDeqd.length > 0) {
27017 for (var i = 0; i < thisDeqd.length; i++) {
27018 deqd.push(thisDeqd[i]);
27019 }
27020 } else {
27021 break;
27022 }
27023 } // callbacks on dequeue
27024
27025
27026 if (deqd.length > 0) {
27027 opts.onDeqd(self, deqd);
27028
27029 if (!willDraw && opts.shouldRedraw(self, deqd, pixelRatio, extent)) {
27030 queueRedraw();
27031 }
27032 }
27033 };
27034
27035 var priority = opts.priority || noop;
27036 r.beforeRender(dequeue, priority(self));
27037 };
27038 }
27039};
27040
27041// Uses keys so elements may share the same cache.
27042
27043var ElementTextureCacheLookup =
27044/*#__PURE__*/
27045function () {
27046 function ElementTextureCacheLookup(getKey) {
27047 var doesEleInvalidateKey = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : falsify;
27048
27049 _classCallCheck(this, ElementTextureCacheLookup);
27050
27051 this.idsByKey = new Map$1();
27052 this.keyForId = new Map$1();
27053 this.cachesByLvl = new Map$1();
27054 this.lvls = [];
27055 this.getKey = getKey;
27056 this.doesEleInvalidateKey = doesEleInvalidateKey;
27057 }
27058
27059 _createClass(ElementTextureCacheLookup, [{
27060 key: "getIdsFor",
27061 value: function getIdsFor(key) {
27062 if (key == null) {
27063 error("Can not get id list for null key");
27064 }
27065
27066 var idsByKey = this.idsByKey;
27067 var ids = this.idsByKey.get(key);
27068
27069 if (!ids) {
27070 ids = new Set$1();
27071 idsByKey.set(key, ids);
27072 }
27073
27074 return ids;
27075 }
27076 }, {
27077 key: "addIdForKey",
27078 value: function addIdForKey(key, id) {
27079 if (key != null) {
27080 this.getIdsFor(key).add(id);
27081 }
27082 }
27083 }, {
27084 key: "deleteIdForKey",
27085 value: function deleteIdForKey(key, id) {
27086 if (key != null) {
27087 this.getIdsFor(key)["delete"](id);
27088 }
27089 }
27090 }, {
27091 key: "getNumberOfIdsForKey",
27092 value: function getNumberOfIdsForKey(key) {
27093 if (key == null) {
27094 return 0;
27095 } else {
27096 return this.getIdsFor(key).size;
27097 }
27098 }
27099 }, {
27100 key: "updateKeyMappingFor",
27101 value: function updateKeyMappingFor(ele) {
27102 var id = ele.id();
27103 var prevKey = this.keyForId.get(id);
27104 var currKey = this.getKey(ele);
27105 this.deleteIdForKey(prevKey, id);
27106 this.addIdForKey(currKey, id);
27107 this.keyForId.set(id, currKey);
27108 }
27109 }, {
27110 key: "deleteKeyMappingFor",
27111 value: function deleteKeyMappingFor(ele) {
27112 var id = ele.id();
27113 var prevKey = this.keyForId.get(id);
27114 this.deleteIdForKey(prevKey, id);
27115 this.keyForId["delete"](id);
27116 }
27117 }, {
27118 key: "keyHasChangedFor",
27119 value: function keyHasChangedFor(ele) {
27120 var id = ele.id();
27121 var prevKey = this.keyForId.get(id);
27122 var newKey = this.getKey(ele);
27123 return prevKey !== newKey;
27124 }
27125 }, {
27126 key: "isInvalid",
27127 value: function isInvalid(ele) {
27128 return this.keyHasChangedFor(ele) || this.doesEleInvalidateKey(ele);
27129 }
27130 }, {
27131 key: "getCachesAt",
27132 value: function getCachesAt(lvl) {
27133 var cachesByLvl = this.cachesByLvl,
27134 lvls = this.lvls;
27135 var caches = cachesByLvl.get(lvl);
27136
27137 if (!caches) {
27138 caches = new Map$1();
27139 cachesByLvl.set(lvl, caches);
27140 lvls.push(lvl);
27141 }
27142
27143 return caches;
27144 }
27145 }, {
27146 key: "getCache",
27147 value: function getCache(key, lvl) {
27148 return this.getCachesAt(lvl).get(key);
27149 }
27150 }, {
27151 key: "get",
27152 value: function get(ele, lvl) {
27153 var key = this.getKey(ele);
27154 var cache = this.getCache(key, lvl); // getting for an element may need to add to the id list b/c eles can share keys
27155
27156 if (cache != null) {
27157 this.updateKeyMappingFor(ele);
27158 }
27159
27160 return cache;
27161 }
27162 }, {
27163 key: "getForCachedKey",
27164 value: function getForCachedKey(ele, lvl) {
27165 var key = this.keyForId.get(ele.id()); // n.b. use cached key, not newly computed key
27166
27167 var cache = this.getCache(key, lvl);
27168 return cache;
27169 }
27170 }, {
27171 key: "hasCache",
27172 value: function hasCache(key, lvl) {
27173 return this.getCachesAt(lvl).has(key);
27174 }
27175 }, {
27176 key: "has",
27177 value: function has(ele, lvl) {
27178 var key = this.getKey(ele);
27179 return this.hasCache(key, lvl);
27180 }
27181 }, {
27182 key: "setCache",
27183 value: function setCache(key, lvl, cache) {
27184 cache.key = key;
27185 this.getCachesAt(lvl).set(key, cache);
27186 }
27187 }, {
27188 key: "set",
27189 value: function set(ele, lvl, cache) {
27190 var key = this.getKey(ele);
27191 this.setCache(key, lvl, cache);
27192 this.updateKeyMappingFor(ele);
27193 }
27194 }, {
27195 key: "deleteCache",
27196 value: function deleteCache(key, lvl) {
27197 this.getCachesAt(lvl)["delete"](key);
27198 }
27199 }, {
27200 key: "delete",
27201 value: function _delete(ele, lvl) {
27202 var key = this.getKey(ele);
27203 this.deleteCache(key, lvl);
27204 }
27205 }, {
27206 key: "invalidateKey",
27207 value: function invalidateKey(key) {
27208 var _this = this;
27209
27210 this.lvls.forEach(function (lvl) {
27211 return _this.deleteCache(key, lvl);
27212 });
27213 } // returns true if no other eles reference the invalidated cache (n.b. other eles may need the cache with the same key)
27214
27215 }, {
27216 key: "invalidate",
27217 value: function invalidate(ele) {
27218 var id = ele.id();
27219 var key = this.keyForId.get(id); // n.b. use stored key rather than current (potential key)
27220
27221 this.deleteKeyMappingFor(ele);
27222 var entireKeyInvalidated = this.doesEleInvalidateKey(ele);
27223
27224 if (entireKeyInvalidated) {
27225 // clear mapping for current key
27226 this.invalidateKey(key);
27227 }
27228
27229 return entireKeyInvalidated || this.getNumberOfIdsForKey(key) === 0;
27230 }
27231 }]);
27232
27233 return ElementTextureCacheLookup;
27234}();
27235
27236var minTxrH = 25; // the size of the texture cache for small height eles (special case)
27237
27238var txrStepH = 50; // the min size of the regular cache, and the size it increases with each step up
27239
27240var minLvl = -4; // when scaling smaller than that we don't need to re-render
27241
27242var maxLvl = 3; // when larger than this scale just render directly (caching is not helpful)
27243
27244var maxZoom = 7.99; // beyond this zoom level, layered textures are not used
27245
27246var eleTxrSpacing = 8; // spacing between elements on textures to avoid blitting overlaps
27247
27248var defTxrWidth = 1024; // default/minimum texture width
27249
27250var maxTxrW = 1024; // the maximum width of a texture
27251
27252var maxTxrH = 1024; // the maximum height of a texture
27253
27254var minUtility = 0.2; // if usage of texture is less than this, it is retired
27255
27256var maxFullness = 0.8; // fullness of texture after which queue removal is checked
27257
27258var maxFullnessChecks = 10; // dequeued after this many checks
27259
27260var deqCost = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27261
27262var deqAvgCost = 0.1; // % of add'l rendering cost compared to average overall redraw time
27263
27264var deqNoDrawCost = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27265
27266var deqFastCost = 0.9; // % of frame time to be used when >60fps
27267
27268var deqRedrawThreshold = 100; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27269
27270var maxDeqSize = 1; // number of eles to dequeue and render at higher texture in each batch
27271
27272var getTxrReasons = {
27273 dequeue: 'dequeue',
27274 downscale: 'downscale',
27275 highQuality: 'highQuality'
27276};
27277var initDefaults = defaults({
27278 getKey: null,
27279 doesEleInvalidateKey: falsify,
27280 drawElement: null,
27281 getBoundingBox: null,
27282 getRotationPoint: null,
27283 getRotationOffset: null,
27284 isVisible: trueify,
27285 allowEdgeTxrCaching: true,
27286 allowParentTxrCaching: true
27287});
27288
27289var ElementTextureCache = function ElementTextureCache(renderer, initOptions) {
27290 var self = this;
27291 self.renderer = renderer;
27292 self.onDequeues = [];
27293 var opts = initDefaults(initOptions);
27294 extend(self, opts);
27295 self.lookup = new ElementTextureCacheLookup(opts.getKey, opts.doesEleInvalidateKey);
27296 self.setupDequeueing();
27297};
27298
27299var ETCp = ElementTextureCache.prototype;
27300ETCp.reasons = getTxrReasons; // the list of textures in which new subtextures for elements can be placed
27301
27302ETCp.getTextureQueue = function (txrH) {
27303 var self = this;
27304 self.eleImgCaches = self.eleImgCaches || {};
27305 return self.eleImgCaches[txrH] = self.eleImgCaches[txrH] || [];
27306}; // the list of usused textures which can be recycled (in use in texture queue)
27307
27308
27309ETCp.getRetiredTextureQueue = function (txrH) {
27310 var self = this;
27311 var rtxtrQs = self.eleImgCaches.retired = self.eleImgCaches.retired || {};
27312 var rtxtrQ = rtxtrQs[txrH] = rtxtrQs[txrH] || [];
27313 return rtxtrQ;
27314}; // queue of element draw requests at different scale levels
27315
27316
27317ETCp.getElementQueue = function () {
27318 var self = this;
27319 var q = self.eleCacheQueue = self.eleCacheQueue || new Heap(function (a, b) {
27320 return b.reqs - a.reqs;
27321 });
27322 return q;
27323}; // queue of element draw requests at different scale levels (element id lookup)
27324
27325
27326ETCp.getElementKeyToQueue = function () {
27327 var self = this;
27328 var k2q = self.eleKeyToCacheQueue = self.eleKeyToCacheQueue || {};
27329 return k2q;
27330};
27331
27332ETCp.getElement = function (ele, bb, pxRatio, lvl, reason) {
27333 var self = this;
27334 var r = this.renderer;
27335 var zoom = r.cy.zoom();
27336 var lookup = this.lookup;
27337
27338 if (!bb || bb.w === 0 || bb.h === 0 || isNaN(bb.w) || isNaN(bb.h) || !ele.visible() || ele.removed()) {
27339 return null;
27340 }
27341
27342 if (!self.allowEdgeTxrCaching && ele.isEdge() || !self.allowParentTxrCaching && ele.isParent()) {
27343 return null;
27344 }
27345
27346 if (lvl == null) {
27347 lvl = Math.ceil(log2(zoom * pxRatio));
27348 }
27349
27350 if (lvl < minLvl) {
27351 lvl = minLvl;
27352 } else if (zoom >= maxZoom || lvl > maxLvl) {
27353 return null;
27354 }
27355
27356 var scale = Math.pow(2, lvl);
27357 var eleScaledH = bb.h * scale;
27358 var eleScaledW = bb.w * scale;
27359 var scaledLabelShown = r.eleTextBiggerThanMin(ele, scale);
27360
27361 if (!this.isVisible(ele, scaledLabelShown)) {
27362 return null;
27363 }
27364
27365 var eleCache = lookup.get(ele, lvl); // if this get was on an unused/invalidated cache, then restore the texture usage metric
27366
27367 if (eleCache && eleCache.invalidated) {
27368 eleCache.invalidated = false;
27369 eleCache.texture.invalidatedWidth -= eleCache.width;
27370 }
27371
27372 if (eleCache) {
27373 return eleCache;
27374 }
27375
27376 var txrH; // which texture height this ele belongs to
27377
27378 if (eleScaledH <= minTxrH) {
27379 txrH = minTxrH;
27380 } else if (eleScaledH <= txrStepH) {
27381 txrH = txrStepH;
27382 } else {
27383 txrH = Math.ceil(eleScaledH / txrStepH) * txrStepH;
27384 }
27385
27386 if (eleScaledH > maxTxrH || eleScaledW > maxTxrW) {
27387 return null; // caching large elements is not efficient
27388 }
27389
27390 var txrQ = self.getTextureQueue(txrH); // first try the second last one in case it has space at the end
27391
27392 var txr = txrQ[txrQ.length - 2];
27393
27394 var addNewTxr = function addNewTxr() {
27395 return self.recycleTexture(txrH, eleScaledW) || self.addTexture(txrH, eleScaledW);
27396 }; // try the last one if there is no second last one
27397
27398
27399 if (!txr) {
27400 txr = txrQ[txrQ.length - 1];
27401 } // if the last one doesn't exist, we need a first one
27402
27403
27404 if (!txr) {
27405 txr = addNewTxr();
27406 } // if there's no room in the current texture, we need a new one
27407
27408
27409 if (txr.width - txr.usedWidth < eleScaledW) {
27410 txr = addNewTxr();
27411 }
27412
27413 var scalableFrom = function scalableFrom(otherCache) {
27414 return otherCache && otherCache.scaledLabelShown === scaledLabelShown;
27415 };
27416
27417 var deqing = reason && reason === getTxrReasons.dequeue;
27418 var highQualityReq = reason && reason === getTxrReasons.highQuality;
27419 var downscaleReq = reason && reason === getTxrReasons.downscale;
27420 var higherCache; // the nearest cache with a higher level
27421
27422 for (var l = lvl + 1; l <= maxLvl; l++) {
27423 var c = lookup.get(ele, l);
27424
27425 if (c) {
27426 higherCache = c;
27427 break;
27428 }
27429 }
27430
27431 var oneUpCache = higherCache && higherCache.level === lvl + 1 ? higherCache : null;
27432
27433 var downscale = function downscale() {
27434 txr.context.drawImage(oneUpCache.texture.canvas, oneUpCache.x, 0, oneUpCache.width, oneUpCache.height, txr.usedWidth, 0, eleScaledW, eleScaledH);
27435 }; // reset ele area in texture
27436
27437
27438 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27439 txr.context.clearRect(txr.usedWidth, 0, eleScaledW, txrH);
27440
27441 if (scalableFrom(oneUpCache)) {
27442 // then we can relatively cheaply rescale the existing image w/o rerendering
27443 downscale();
27444 } else if (scalableFrom(higherCache)) {
27445 // then use the higher cache for now and queue the next level down
27446 // to cheaply scale towards the smaller level
27447 if (highQualityReq) {
27448 for (var _l = higherCache.level; _l > lvl; _l--) {
27449 oneUpCache = self.getElement(ele, bb, pxRatio, _l, getTxrReasons.downscale);
27450 }
27451
27452 downscale();
27453 } else {
27454 self.queueElement(ele, higherCache.level - 1);
27455 return higherCache;
27456 }
27457 } else {
27458 var lowerCache; // the nearest cache with a lower level
27459
27460 if (!deqing && !highQualityReq && !downscaleReq) {
27461 for (var _l2 = lvl - 1; _l2 >= minLvl; _l2--) {
27462 var _c = lookup.get(ele, _l2);
27463
27464 if (_c) {
27465 lowerCache = _c;
27466 break;
27467 }
27468 }
27469 }
27470
27471 if (scalableFrom(lowerCache)) {
27472 // then use the lower quality cache for now and queue the better one for later
27473 self.queueElement(ele, lvl);
27474 return lowerCache;
27475 }
27476
27477 txr.context.translate(txr.usedWidth, 0);
27478 txr.context.scale(scale, scale);
27479 this.drawElement(txr.context, ele, bb, scaledLabelShown, false);
27480 txr.context.scale(1 / scale, 1 / scale);
27481 txr.context.translate(-txr.usedWidth, 0);
27482 }
27483
27484 eleCache = {
27485 x: txr.usedWidth,
27486 texture: txr,
27487 level: lvl,
27488 scale: scale,
27489 width: eleScaledW,
27490 height: eleScaledH,
27491 scaledLabelShown: scaledLabelShown
27492 };
27493 txr.usedWidth += Math.ceil(eleScaledW + eleTxrSpacing);
27494 txr.eleCaches.push(eleCache);
27495 lookup.set(ele, lvl, eleCache);
27496 self.checkTextureFullness(txr);
27497 return eleCache;
27498};
27499
27500ETCp.invalidateElements = function (eles) {
27501 for (var i = 0; i < eles.length; i++) {
27502 this.invalidateElement(eles[i]);
27503 }
27504};
27505
27506ETCp.invalidateElement = function (ele) {
27507 var self = this;
27508 var lookup = self.lookup;
27509 var caches = [];
27510 var invalid = lookup.isInvalid(ele);
27511
27512 if (!invalid) {
27513 return; // override the invalidation request if the element key has not changed
27514 }
27515
27516 for (var lvl = minLvl; lvl <= maxLvl; lvl++) {
27517 var cache = lookup.getForCachedKey(ele, lvl);
27518
27519 if (cache) {
27520 caches.push(cache);
27521 }
27522 }
27523
27524 var noOtherElesUseCache = lookup.invalidate(ele);
27525
27526 if (noOtherElesUseCache) {
27527 for (var i = 0; i < caches.length; i++) {
27528 var _cache = caches[i];
27529 var txr = _cache.texture; // remove space from the texture it belongs to
27530
27531 txr.invalidatedWidth += _cache.width; // mark the cache as invalidated
27532
27533 _cache.invalidated = true; // retire the texture if its utility is low
27534
27535 self.checkTextureUtility(txr);
27536 }
27537 } // remove from queue since the old req was for the old state
27538
27539
27540 self.removeFromQueue(ele);
27541};
27542
27543ETCp.checkTextureUtility = function (txr) {
27544 // invalidate all entries in the cache if the cache size is small
27545 if (txr.invalidatedWidth >= minUtility * txr.width) {
27546 this.retireTexture(txr);
27547 }
27548};
27549
27550ETCp.checkTextureFullness = function (txr) {
27551 // if texture has been mostly filled and passed over several times, remove
27552 // it from the queue so we don't need to waste time looking at it to put new things
27553 var self = this;
27554 var txrQ = self.getTextureQueue(txr.height);
27555
27556 if (txr.usedWidth / txr.width > maxFullness && txr.fullnessChecks >= maxFullnessChecks) {
27557 removeFromArray(txrQ, txr);
27558 } else {
27559 txr.fullnessChecks++;
27560 }
27561};
27562
27563ETCp.retireTexture = function (txr) {
27564 var self = this;
27565 var txrH = txr.height;
27566 var txrQ = self.getTextureQueue(txrH);
27567 var lookup = this.lookup; // retire the texture from the active / searchable queue:
27568
27569 removeFromArray(txrQ, txr);
27570 txr.retired = true; // remove the refs from the eles to the caches:
27571
27572 var eleCaches = txr.eleCaches;
27573
27574 for (var i = 0; i < eleCaches.length; i++) {
27575 var eleCache = eleCaches[i];
27576 lookup.deleteCache(eleCache.key, eleCache.level);
27577 }
27578
27579 clearArray(eleCaches); // add the texture to a retired queue so it can be recycled in future:
27580
27581 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27582 rtxtrQ.push(txr);
27583};
27584
27585ETCp.addTexture = function (txrH, minW) {
27586 var self = this;
27587 var txrQ = self.getTextureQueue(txrH);
27588 var txr = {};
27589 txrQ.push(txr);
27590 txr.eleCaches = [];
27591 txr.height = txrH;
27592 txr.width = Math.max(defTxrWidth, minW);
27593 txr.usedWidth = 0;
27594 txr.invalidatedWidth = 0;
27595 txr.fullnessChecks = 0;
27596 txr.canvas = self.renderer.makeOffscreenCanvas(txr.width, txr.height);
27597 txr.context = txr.canvas.getContext('2d');
27598 return txr;
27599};
27600
27601ETCp.recycleTexture = function (txrH, minW) {
27602 var self = this;
27603 var txrQ = self.getTextureQueue(txrH);
27604 var rtxtrQ = self.getRetiredTextureQueue(txrH);
27605
27606 for (var i = 0; i < rtxtrQ.length; i++) {
27607 var txr = rtxtrQ[i];
27608
27609 if (txr.width >= minW) {
27610 txr.retired = false;
27611 txr.usedWidth = 0;
27612 txr.invalidatedWidth = 0;
27613 txr.fullnessChecks = 0;
27614 clearArray(txr.eleCaches);
27615 txr.context.setTransform(1, 0, 0, 1, 0, 0);
27616 txr.context.clearRect(0, 0, txr.width, txr.height);
27617 removeFromArray(rtxtrQ, txr);
27618 txrQ.push(txr);
27619 return txr;
27620 }
27621 }
27622};
27623
27624ETCp.queueElement = function (ele, lvl) {
27625 var self = this;
27626 var q = self.getElementQueue();
27627 var k2q = self.getElementKeyToQueue();
27628 var key = this.getKey(ele);
27629 var existingReq = k2q[key];
27630
27631 if (existingReq) {
27632 // use the max lvl b/c in between lvls are cheap to make
27633 existingReq.level = Math.max(existingReq.level, lvl);
27634 existingReq.eles.merge(ele);
27635 existingReq.reqs++;
27636 q.updateItem(existingReq);
27637 } else {
27638 var req = {
27639 eles: ele.spawn().merge(ele),
27640 level: lvl,
27641 reqs: 1,
27642 key: key
27643 };
27644 q.push(req);
27645 k2q[key] = req;
27646 }
27647};
27648
27649ETCp.dequeue = function (pxRatio
27650/*, extent*/
27651) {
27652 var self = this;
27653 var q = self.getElementQueue();
27654 var k2q = self.getElementKeyToQueue();
27655 var dequeued = [];
27656 var lookup = self.lookup;
27657
27658 for (var i = 0; i < maxDeqSize; i++) {
27659 if (q.size() > 0) {
27660 var req = q.pop();
27661 var key = req.key;
27662 var ele = req.eles[0]; // all eles have the same key
27663
27664 var cacheExists = lookup.hasCache(ele, req.level); // clear out the key to req lookup
27665
27666 k2q[key] = null; // dequeueing isn't necessary with an existing cache
27667
27668 if (cacheExists) {
27669 continue;
27670 }
27671
27672 dequeued.push(req);
27673 var bb = self.getBoundingBox(ele);
27674 self.getElement(ele, bb, pxRatio, req.level, getTxrReasons.dequeue);
27675 } else {
27676 break;
27677 }
27678 }
27679
27680 return dequeued;
27681};
27682
27683ETCp.removeFromQueue = function (ele) {
27684 var self = this;
27685 var q = self.getElementQueue();
27686 var k2q = self.getElementKeyToQueue();
27687 var key = this.getKey(ele);
27688 var req = k2q[key];
27689
27690 if (req != null) {
27691 if (req.eles.length === 1) {
27692 // remove if last ele in the req
27693 // bring to front of queue
27694 req.reqs = MAX_INT;
27695 q.updateItem(req);
27696 q.pop(); // remove from queue
27697
27698 k2q[key] = null; // remove from lookup map
27699 } else {
27700 // otherwise just remove ele from req
27701 req.eles.unmerge(ele);
27702 }
27703 }
27704};
27705
27706ETCp.onDequeue = function (fn) {
27707 this.onDequeues.push(fn);
27708};
27709
27710ETCp.offDequeue = function (fn) {
27711 removeFromArray(this.onDequeues, fn);
27712};
27713
27714ETCp.setupDequeueing = defs.setupDequeueing({
27715 deqRedrawThreshold: deqRedrawThreshold,
27716 deqCost: deqCost,
27717 deqAvgCost: deqAvgCost,
27718 deqNoDrawCost: deqNoDrawCost,
27719 deqFastCost: deqFastCost,
27720 deq: function deq(self, pxRatio, extent) {
27721 return self.dequeue(pxRatio, extent);
27722 },
27723 onDeqd: function onDeqd(self, deqd) {
27724 for (var i = 0; i < self.onDequeues.length; i++) {
27725 var fn = self.onDequeues[i];
27726 fn(deqd);
27727 }
27728 },
27729 shouldRedraw: function shouldRedraw(self, deqd, pxRatio, extent) {
27730 for (var i = 0; i < deqd.length; i++) {
27731 var eles = deqd[i].eles;
27732
27733 for (var j = 0; j < eles.length; j++) {
27734 var bb = eles[j].boundingBox();
27735
27736 if (boundingBoxesIntersect(bb, extent)) {
27737 return true;
27738 }
27739 }
27740 }
27741
27742 return false;
27743 },
27744 priority: function priority(self) {
27745 return self.renderer.beforeRenderPriorities.eleTxrDeq;
27746 }
27747});
27748
27749var defNumLayers = 1; // default number of layers to use
27750
27751var minLvl$1 = -4; // when scaling smaller than that we don't need to re-render
27752
27753var maxLvl$1 = 2; // when larger than this scale just render directly (caching is not helpful)
27754
27755var maxZoom$1 = 3.99; // beyond this zoom level, layered textures are not used
27756
27757var deqRedrawThreshold$1 = 50; // time to batch redraws together from dequeueing to allow more dequeueing calcs to happen in the meanwhile
27758
27759var refineEleDebounceTime = 50; // time to debounce sharper ele texture updates
27760
27761var deqCost$1 = 0.15; // % of add'l rendering cost allowed for dequeuing ele caches each frame
27762
27763var deqAvgCost$1 = 0.1; // % of add'l rendering cost compared to average overall redraw time
27764
27765var deqNoDrawCost$1 = 0.9; // % of avg frame time that can be used for dequeueing when not drawing
27766
27767var deqFastCost$1 = 0.9; // % of frame time to be used when >60fps
27768
27769var maxDeqSize$1 = 1; // number of eles to dequeue and render at higher texture in each batch
27770
27771var invalidThreshold = 250; // time threshold for disabling b/c of invalidations
27772
27773var maxLayerArea = 4000 * 4000; // layers can't be bigger than this
27774
27775var useHighQualityEleTxrReqs = true; // whether to use high quality ele txr requests (generally faster and cheaper in the longterm)
27776// var log = function(){ console.log.apply( console, arguments ); };
27777
27778var LayeredTextureCache = function LayeredTextureCache(renderer) {
27779 var self = this;
27780 var r = self.renderer = renderer;
27781 var cy = r.cy;
27782 self.layersByLevel = {}; // e.g. 2 => [ layer1, layer2, ..., layerN ]
27783
27784 self.firstGet = true;
27785 self.lastInvalidationTime = performanceNow() - 2 * invalidThreshold;
27786 self.skipping = false;
27787 self.eleTxrDeqs = cy.collection();
27788 self.scheduleElementRefinement = util(function () {
27789 self.refineElementTextures(self.eleTxrDeqs);
27790 self.eleTxrDeqs.unmerge(self.eleTxrDeqs);
27791 }, refineEleDebounceTime);
27792 r.beforeRender(function (willDraw, now) {
27793 if (now - self.lastInvalidationTime <= invalidThreshold) {
27794 self.skipping = true;
27795 } else {
27796 self.skipping = false;
27797 }
27798 }, r.beforeRenderPriorities.lyrTxrSkip);
27799
27800 var qSort = function qSort(a, b) {
27801 return b.reqs - a.reqs;
27802 };
27803
27804 self.layersQueue = new Heap(qSort);
27805 self.setupDequeueing();
27806};
27807
27808var LTCp = LayeredTextureCache.prototype;
27809var layerIdPool = 0;
27810var MAX_INT$1 = Math.pow(2, 53) - 1;
27811
27812LTCp.makeLayer = function (bb, lvl) {
27813 var scale = Math.pow(2, lvl);
27814 var w = Math.ceil(bb.w * scale);
27815 var h = Math.ceil(bb.h * scale);
27816 var canvas = this.renderer.makeOffscreenCanvas(w, h);
27817 var layer = {
27818 id: layerIdPool = ++layerIdPool % MAX_INT$1,
27819 bb: bb,
27820 level: lvl,
27821 width: w,
27822 height: h,
27823 canvas: canvas,
27824 context: canvas.getContext('2d'),
27825 eles: [],
27826 elesQueue: [],
27827 reqs: 0
27828 }; // log('make layer %s with w %s and h %s and lvl %s', layer.id, layer.width, layer.height, layer.level);
27829
27830 var cxt = layer.context;
27831 var dx = -layer.bb.x1;
27832 var dy = -layer.bb.y1; // do the transform on creation to save cycles (it's the same for all eles)
27833
27834 cxt.scale(scale, scale);
27835 cxt.translate(dx, dy);
27836 return layer;
27837};
27838
27839LTCp.getLayers = function (eles, pxRatio, lvl) {
27840 var self = this;
27841 var r = self.renderer;
27842 var cy = r.cy;
27843 var zoom = cy.zoom();
27844 var firstGet = self.firstGet;
27845 self.firstGet = false; // log('--\nget layers with %s eles', eles.length);
27846 //log eles.map(function(ele){ return ele.id() }) );
27847
27848 if (lvl == null) {
27849 lvl = Math.ceil(log2(zoom * pxRatio));
27850
27851 if (lvl < minLvl$1) {
27852 lvl = minLvl$1;
27853 } else if (zoom >= maxZoom$1 || lvl > maxLvl$1) {
27854 return null;
27855 }
27856 }
27857
27858 self.validateLayersElesOrdering(lvl, eles);
27859 var layersByLvl = self.layersByLevel;
27860 var scale = Math.pow(2, lvl);
27861 var layers = layersByLvl[lvl] = layersByLvl[lvl] || [];
27862 var bb;
27863 var lvlComplete = self.levelIsComplete(lvl, eles);
27864 var tmpLayers;
27865
27866 var checkTempLevels = function checkTempLevels() {
27867 var canUseAsTmpLvl = function canUseAsTmpLvl(l) {
27868 self.validateLayersElesOrdering(l, eles);
27869
27870 if (self.levelIsComplete(l, eles)) {
27871 tmpLayers = layersByLvl[l];
27872 return true;
27873 }
27874 };
27875
27876 var checkLvls = function checkLvls(dir) {
27877 if (tmpLayers) {
27878 return;
27879 }
27880
27881 for (var l = lvl + dir; minLvl$1 <= l && l <= maxLvl$1; l += dir) {
27882 if (canUseAsTmpLvl(l)) {
27883 break;
27884 }
27885 }
27886 };
27887
27888 checkLvls(+1);
27889 checkLvls(-1); // remove the invalid layers; they will be replaced as needed later in this function
27890
27891 for (var i = layers.length - 1; i >= 0; i--) {
27892 var layer = layers[i];
27893
27894 if (layer.invalid) {
27895 removeFromArray(layers, layer);
27896 }
27897 }
27898 };
27899
27900 if (!lvlComplete) {
27901 // if the current level is incomplete, then use the closest, best quality layerset temporarily
27902 // and later queue the current layerset so we can get the proper quality level soon
27903 checkTempLevels();
27904 } else {
27905 // log('level complete, using existing layers\n--');
27906 return layers;
27907 }
27908
27909 var getBb = function getBb() {
27910 if (!bb) {
27911 bb = makeBoundingBox();
27912
27913 for (var i = 0; i < eles.length; i++) {
27914 updateBoundingBox(bb, eles[i].boundingBox());
27915 }
27916 }
27917
27918 return bb;
27919 };
27920
27921 var makeLayer = function makeLayer(opts) {
27922 opts = opts || {};
27923 var after = opts.after;
27924 getBb();
27925 var area = bb.w * scale * (bb.h * scale);
27926
27927 if (area > maxLayerArea) {
27928 return null;
27929 }
27930
27931 var layer = self.makeLayer(bb, lvl);
27932
27933 if (after != null) {
27934 var index = layers.indexOf(after) + 1;
27935 layers.splice(index, 0, layer);
27936 } else if (opts.insert === undefined || opts.insert) {
27937 // no after specified => first layer made so put at start
27938 layers.unshift(layer);
27939 } // if( tmpLayers ){
27940 //self.queueLayer( layer );
27941 // }
27942
27943
27944 return layer;
27945 };
27946
27947 if (self.skipping && !firstGet) {
27948 // log('skip layers');
27949 return null;
27950 } // log('do layers');
27951
27952
27953 var layer = null;
27954 var maxElesPerLayer = eles.length / defNumLayers;
27955 var allowLazyQueueing = !firstGet;
27956
27957 for (var i = 0; i < eles.length; i++) {
27958 var ele = eles[i];
27959 var rs = ele._private.rscratch;
27960 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {}; // log('look at ele', ele.id());
27961
27962 var existingLayer = caches[lvl];
27963
27964 if (existingLayer) {
27965 // reuse layer for later eles
27966 // log('reuse layer for', ele.id());
27967 layer = existingLayer;
27968 continue;
27969 }
27970
27971 if (!layer || layer.eles.length >= maxElesPerLayer || !boundingBoxInBoundingBox(layer.bb, ele.boundingBox())) {
27972 // log('make new layer for ele %s', ele.id());
27973 layer = makeLayer({
27974 insert: true,
27975 after: layer
27976 }); // if now layer can be built then we can't use layers at this level
27977
27978 if (!layer) {
27979 return null;
27980 } // log('new layer with id %s', layer.id);
27981
27982 }
27983
27984 if (tmpLayers || allowLazyQueueing) {
27985 // log('queue ele %s in layer %s', ele.id(), layer.id);
27986 self.queueLayer(layer, ele);
27987 } else {
27988 // log('draw ele %s in layer %s', ele.id(), layer.id);
27989 self.drawEleInLayer(layer, ele, lvl, pxRatio);
27990 }
27991
27992 layer.eles.push(ele);
27993 caches[lvl] = layer;
27994 } // log('--');
27995
27996
27997 if (tmpLayers) {
27998 // then we only queued the current layerset and can't draw it yet
27999 return tmpLayers;
28000 }
28001
28002 if (allowLazyQueueing) {
28003 // log('lazy queue level', lvl);
28004 return null;
28005 }
28006
28007 return layers;
28008}; // a layer may want to use an ele cache of a higher level to avoid blurriness
28009// so the layer level might not equal the ele level
28010
28011
28012LTCp.getEleLevelForLayerLevel = function (lvl, pxRatio) {
28013 return lvl;
28014};
28015
28016LTCp.drawEleInLayer = function (layer, ele, lvl, pxRatio) {
28017 var self = this;
28018 var r = this.renderer;
28019 var context = layer.context;
28020 var bb = ele.boundingBox();
28021
28022 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28023 return;
28024 }
28025
28026 lvl = self.getEleLevelForLayerLevel(lvl, pxRatio);
28027
28028 {
28029 r.setImgSmoothing(context, false);
28030 }
28031
28032 {
28033 r.drawCachedElement(context, ele, null, null, lvl, useHighQualityEleTxrReqs);
28034 }
28035
28036 {
28037 r.setImgSmoothing(context, true);
28038 }
28039};
28040
28041LTCp.levelIsComplete = function (lvl, eles) {
28042 var self = this;
28043 var layers = self.layersByLevel[lvl];
28044
28045 if (!layers || layers.length === 0) {
28046 return false;
28047 }
28048
28049 var numElesInLayers = 0;
28050
28051 for (var i = 0; i < layers.length; i++) {
28052 var layer = layers[i]; // if there are any eles needed to be drawn yet, the level is not complete
28053
28054 if (layer.reqs > 0) {
28055 return false;
28056 } // if the layer is invalid, the level is not complete
28057
28058
28059 if (layer.invalid) {
28060 return false;
28061 }
28062
28063 numElesInLayers += layer.eles.length;
28064 } // we should have exactly the number of eles passed in to be complete
28065
28066
28067 if (numElesInLayers !== eles.length) {
28068 return false;
28069 }
28070
28071 return true;
28072};
28073
28074LTCp.validateLayersElesOrdering = function (lvl, eles) {
28075 var layers = this.layersByLevel[lvl];
28076
28077 if (!layers) {
28078 return;
28079 } // if in a layer the eles are not in the same order, then the layer is invalid
28080 // (i.e. there is an ele in between the eles in the layer)
28081
28082
28083 for (var i = 0; i < layers.length; i++) {
28084 var layer = layers[i];
28085 var offset = -1; // find the offset
28086
28087 for (var j = 0; j < eles.length; j++) {
28088 if (layer.eles[0] === eles[j]) {
28089 offset = j;
28090 break;
28091 }
28092 }
28093
28094 if (offset < 0) {
28095 // then the layer has nonexistant elements and is invalid
28096 this.invalidateLayer(layer);
28097 continue;
28098 } // the eles in the layer must be in the same continuous order, else the layer is invalid
28099
28100
28101 var o = offset;
28102
28103 for (var j = 0; j < layer.eles.length; j++) {
28104 if (layer.eles[j] !== eles[o + j]) {
28105 // log('invalidate based on ordering', layer.id);
28106 this.invalidateLayer(layer);
28107 break;
28108 }
28109 }
28110 }
28111};
28112
28113LTCp.updateElementsInLayers = function (eles, update) {
28114 var self = this;
28115 var isEles = element(eles[0]); // collect udpated elements (cascaded from the layers) and update each
28116 // layer itself along the way
28117
28118 for (var i = 0; i < eles.length; i++) {
28119 var req = isEles ? null : eles[i];
28120 var ele = isEles ? eles[i] : eles[i].ele;
28121 var rs = ele._private.rscratch;
28122 var caches = rs.imgLayerCaches = rs.imgLayerCaches || {};
28123
28124 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28125 var layer = caches[l];
28126
28127 if (!layer) {
28128 continue;
28129 } // if update is a request from the ele cache, then it affects only
28130 // the matching level
28131
28132
28133 if (req && self.getEleLevelForLayerLevel(layer.level) !== req.level) {
28134 continue;
28135 }
28136
28137 update(layer, ele, req);
28138 }
28139 }
28140};
28141
28142LTCp.haveLayers = function () {
28143 var self = this;
28144 var haveLayers = false;
28145
28146 for (var l = minLvl$1; l <= maxLvl$1; l++) {
28147 var layers = self.layersByLevel[l];
28148
28149 if (layers && layers.length > 0) {
28150 haveLayers = true;
28151 break;
28152 }
28153 }
28154
28155 return haveLayers;
28156};
28157
28158LTCp.invalidateElements = function (eles) {
28159 var self = this;
28160
28161 if (eles.length === 0) {
28162 return;
28163 }
28164
28165 self.lastInvalidationTime = performanceNow(); // log('update invalidate layer time from eles');
28166
28167 if (eles.length === 0 || !self.haveLayers()) {
28168 return;
28169 }
28170
28171 self.updateElementsInLayers(eles, function invalAssocLayers(layer, ele, req) {
28172 self.invalidateLayer(layer);
28173 });
28174};
28175
28176LTCp.invalidateLayer = function (layer) {
28177 // log('update invalidate layer time');
28178 this.lastInvalidationTime = performanceNow();
28179
28180 if (layer.invalid) {
28181 return;
28182 } // save cycles
28183
28184
28185 var lvl = layer.level;
28186 var eles = layer.eles;
28187 var layers = this.layersByLevel[lvl]; // log('invalidate layer', layer.id );
28188
28189 removeFromArray(layers, layer); // layer.eles = [];
28190
28191 layer.elesQueue = [];
28192 layer.invalid = true;
28193
28194 if (layer.replacement) {
28195 layer.replacement.invalid = true;
28196 }
28197
28198 for (var i = 0; i < eles.length; i++) {
28199 var caches = eles[i]._private.rscratch.imgLayerCaches;
28200
28201 if (caches) {
28202 caches[lvl] = null;
28203 }
28204 }
28205};
28206
28207LTCp.refineElementTextures = function (eles) {
28208 var self = this; // log('refine', eles.length);
28209
28210 self.updateElementsInLayers(eles, function refineEachEle(layer, ele, req) {
28211 var rLyr = layer.replacement;
28212
28213 if (!rLyr) {
28214 rLyr = layer.replacement = self.makeLayer(layer.bb, layer.level);
28215 rLyr.replaces = layer;
28216 rLyr.eles = layer.eles; // log('make replacement layer %s for %s with level %s', rLyr.id, layer.id, rLyr.level);
28217 }
28218
28219 if (!rLyr.reqs) {
28220 for (var i = 0; i < rLyr.eles.length; i++) {
28221 self.queueLayer(rLyr, rLyr.eles[i]);
28222 } // log('queue replacement layer refinement', rLyr.id);
28223
28224 }
28225 });
28226};
28227
28228LTCp.enqueueElementRefinement = function (ele) {
28229
28230 this.eleTxrDeqs.merge(ele);
28231 this.scheduleElementRefinement();
28232};
28233
28234LTCp.queueLayer = function (layer, ele) {
28235 var self = this;
28236 var q = self.layersQueue;
28237 var elesQ = layer.elesQueue;
28238 var hasId = elesQ.hasId = elesQ.hasId || {}; // if a layer is going to be replaced, queuing is a waste of time
28239
28240 if (layer.replacement) {
28241 return;
28242 }
28243
28244 if (ele) {
28245 if (hasId[ele.id()]) {
28246 return;
28247 }
28248
28249 elesQ.push(ele);
28250 hasId[ele.id()] = true;
28251 }
28252
28253 if (layer.reqs) {
28254 layer.reqs++;
28255 q.updateItem(layer);
28256 } else {
28257 layer.reqs = 1;
28258 q.push(layer);
28259 }
28260};
28261
28262LTCp.dequeue = function (pxRatio) {
28263 var self = this;
28264 var q = self.layersQueue;
28265 var deqd = [];
28266 var eleDeqs = 0;
28267
28268 while (eleDeqs < maxDeqSize$1) {
28269 if (q.size() === 0) {
28270 break;
28271 }
28272
28273 var layer = q.peek(); // if a layer has been or will be replaced, then don't waste time with it
28274
28275 if (layer.replacement) {
28276 // log('layer %s in queue skipped b/c it already has a replacement', layer.id);
28277 q.pop();
28278 continue;
28279 } // if this is a replacement layer that has been superceded, then forget it
28280
28281
28282 if (layer.replaces && layer !== layer.replaces.replacement) {
28283 // log('layer is no longer the most uptodate replacement; dequeued', layer.id)
28284 q.pop();
28285 continue;
28286 }
28287
28288 if (layer.invalid) {
28289 // log('replacement layer %s is invalid; dequeued', layer.id);
28290 q.pop();
28291 continue;
28292 }
28293
28294 var ele = layer.elesQueue.shift();
28295
28296 if (ele) {
28297 // log('dequeue layer %s', layer.id);
28298 self.drawEleInLayer(layer, ele, layer.level, pxRatio);
28299 eleDeqs++;
28300 }
28301
28302 if (deqd.length === 0) {
28303 // we need only one entry in deqd to queue redrawing etc
28304 deqd.push(true);
28305 } // if the layer has all its eles done, then remove from the queue
28306
28307
28308 if (layer.elesQueue.length === 0) {
28309 q.pop();
28310 layer.reqs = 0; // log('dequeue of layer %s complete', layer.id);
28311 // when a replacement layer is dequeued, it replaces the old layer in the level
28312
28313 if (layer.replaces) {
28314 self.applyLayerReplacement(layer);
28315 }
28316
28317 self.requestRedraw();
28318 }
28319 }
28320
28321 return deqd;
28322};
28323
28324LTCp.applyLayerReplacement = function (layer) {
28325 var self = this;
28326 var layersInLevel = self.layersByLevel[layer.level];
28327 var replaced = layer.replaces;
28328 var index = layersInLevel.indexOf(replaced); // if the replaced layer is not in the active list for the level, then replacing
28329 // refs would be a mistake (i.e. overwriting the true active layer)
28330
28331 if (index < 0 || replaced.invalid) {
28332 // log('replacement layer would have no effect', layer.id);
28333 return;
28334 }
28335
28336 layersInLevel[index] = layer; // replace level ref
28337 // replace refs in eles
28338
28339 for (var i = 0; i < layer.eles.length; i++) {
28340 var _p = layer.eles[i]._private;
28341 var cache = _p.imgLayerCaches = _p.imgLayerCaches || {};
28342
28343 if (cache) {
28344 cache[layer.level] = layer;
28345 }
28346 } // log('apply replacement layer %s over %s', layer.id, replaced.id);
28347
28348
28349 self.requestRedraw();
28350};
28351
28352LTCp.requestRedraw = util(function () {
28353 var r = this.renderer;
28354 r.redrawHint('eles', true);
28355 r.redrawHint('drag', true);
28356 r.redraw();
28357}, 100);
28358LTCp.setupDequeueing = defs.setupDequeueing({
28359 deqRedrawThreshold: deqRedrawThreshold$1,
28360 deqCost: deqCost$1,
28361 deqAvgCost: deqAvgCost$1,
28362 deqNoDrawCost: deqNoDrawCost$1,
28363 deqFastCost: deqFastCost$1,
28364 deq: function deq(self, pxRatio) {
28365 return self.dequeue(pxRatio);
28366 },
28367 onDeqd: noop,
28368 shouldRedraw: trueify,
28369 priority: function priority(self) {
28370 return self.renderer.beforeRenderPriorities.lyrTxrDeq;
28371 }
28372});
28373
28374var CRp = {};
28375var impl;
28376
28377function polygon(context, points) {
28378 for (var i = 0; i < points.length; i++) {
28379 var pt = points[i];
28380 context.lineTo(pt.x, pt.y);
28381 }
28382}
28383
28384function triangleBackcurve(context, points, controlPoint) {
28385 var firstPt;
28386
28387 for (var i = 0; i < points.length; i++) {
28388 var pt = points[i];
28389
28390 if (i === 0) {
28391 firstPt = pt;
28392 }
28393
28394 context.lineTo(pt.x, pt.y);
28395 }
28396
28397 context.quadraticCurveTo(controlPoint.x, controlPoint.y, firstPt.x, firstPt.y);
28398}
28399
28400function triangleTee(context, trianglePoints, teePoints) {
28401 if (context.beginPath) {
28402 context.beginPath();
28403 }
28404
28405 var triPts = trianglePoints;
28406
28407 for (var i = 0; i < triPts.length; i++) {
28408 var pt = triPts[i];
28409 context.lineTo(pt.x, pt.y);
28410 }
28411
28412 var teePts = teePoints;
28413 var firstTeePt = teePoints[0];
28414 context.moveTo(firstTeePt.x, firstTeePt.y);
28415
28416 for (var i = 1; i < teePts.length; i++) {
28417 var pt = teePts[i];
28418 context.lineTo(pt.x, pt.y);
28419 }
28420
28421 if (context.closePath) {
28422 context.closePath();
28423 }
28424}
28425
28426function circleTriangle(context, trianglePoints, rx, ry, r) {
28427 if (context.beginPath) {
28428 context.beginPath();
28429 }
28430
28431 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28432 var triPts = trianglePoints;
28433 var firstTrPt = triPts[0];
28434 context.moveTo(firstTrPt.x, firstTrPt.y);
28435
28436 for (var i = 0; i < triPts.length; i++) {
28437 var pt = triPts[i];
28438 context.lineTo(pt.x, pt.y);
28439 }
28440
28441 if (context.closePath) {
28442 context.closePath();
28443 }
28444}
28445
28446function circle(context, rx, ry, r) {
28447 context.arc(rx, ry, r, 0, Math.PI * 2, false);
28448}
28449
28450CRp.arrowShapeImpl = function (name) {
28451 return (impl || (impl = {
28452 'polygon': polygon,
28453 'triangle-backcurve': triangleBackcurve,
28454 'triangle-tee': triangleTee,
28455 'circle-triangle': circleTriangle,
28456 'triangle-cross': triangleTee,
28457 'circle': circle
28458 }))[name];
28459};
28460
28461var CRp$1 = {};
28462
28463CRp$1.drawElement = function (context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity) {
28464 var r = this;
28465
28466 if (ele.isNode()) {
28467 r.drawNode(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28468 } else {
28469 r.drawEdge(context, ele, shiftToOriginWithBb, showLabel, showOverlay, showOpacity);
28470 }
28471};
28472
28473CRp$1.drawElementOverlay = function (context, ele) {
28474 var r = this;
28475
28476 if (ele.isNode()) {
28477 r.drawNodeOverlay(context, ele);
28478 } else {
28479 r.drawEdgeOverlay(context, ele);
28480 }
28481};
28482
28483CRp$1.drawCachedElementPortion = function (context, ele, eleTxrCache, pxRatio, lvl, reason, getRotation, getOpacity) {
28484 var r = this;
28485 var bb = eleTxrCache.getBoundingBox(ele);
28486
28487 if (bb.w === 0 || bb.h === 0) {
28488 return;
28489 } // ignore zero size case
28490
28491
28492 var eleCache = eleTxrCache.getElement(ele, bb, pxRatio, lvl, reason);
28493
28494 if (eleCache != null) {
28495 var opacity = getOpacity(r, ele);
28496
28497 if (opacity === 0) {
28498 return;
28499 }
28500
28501 var theta = getRotation(r, ele);
28502 var x1 = bb.x1,
28503 y1 = bb.y1,
28504 w = bb.w,
28505 h = bb.h;
28506 var x, y, sx, sy, smooth;
28507
28508 if (theta !== 0) {
28509 var rotPt = eleTxrCache.getRotationPoint(ele);
28510 sx = rotPt.x;
28511 sy = rotPt.y;
28512 context.translate(sx, sy);
28513 context.rotate(theta);
28514 smooth = r.getImgSmoothing(context);
28515
28516 if (!smooth) {
28517 r.setImgSmoothing(context, true);
28518 }
28519
28520 var off = eleTxrCache.getRotationOffset(ele);
28521 x = off.x;
28522 y = off.y;
28523 } else {
28524 x = x1;
28525 y = y1;
28526 }
28527
28528 var oldGlobalAlpha;
28529
28530 if (opacity !== 1) {
28531 oldGlobalAlpha = context.globalAlpha;
28532 context.globalAlpha = oldGlobalAlpha * opacity;
28533 }
28534
28535 context.drawImage(eleCache.texture.canvas, eleCache.x, 0, eleCache.width, eleCache.height, x, y, w, h);
28536
28537 if (opacity !== 1) {
28538 context.globalAlpha = oldGlobalAlpha;
28539 }
28540
28541 if (theta !== 0) {
28542 context.rotate(-theta);
28543 context.translate(-sx, -sy);
28544
28545 if (!smooth) {
28546 r.setImgSmoothing(context, false);
28547 }
28548 }
28549 } else {
28550 eleTxrCache.drawElement(context, ele); // direct draw fallback
28551 }
28552};
28553
28554var getZeroRotation = function getZeroRotation() {
28555 return 0;
28556};
28557
28558var getLabelRotation = function getLabelRotation(r, ele) {
28559 return r.getTextAngle(ele, null);
28560};
28561
28562var getSourceLabelRotation = function getSourceLabelRotation(r, ele) {
28563 return r.getTextAngle(ele, 'source');
28564};
28565
28566var getTargetLabelRotation = function getTargetLabelRotation(r, ele) {
28567 return r.getTextAngle(ele, 'target');
28568};
28569
28570var getOpacity = function getOpacity(r, ele) {
28571 return ele.effectiveOpacity();
28572};
28573
28574var getTextOpacity = function getTextOpacity(e, ele) {
28575 return ele.pstyle('text-opacity').pfValue * ele.effectiveOpacity();
28576};
28577
28578CRp$1.drawCachedElement = function (context, ele, pxRatio, extent, lvl, requestHighQuality) {
28579 var r = this;
28580 var _r$data = r.data,
28581 eleTxrCache = _r$data.eleTxrCache,
28582 lblTxrCache = _r$data.lblTxrCache,
28583 slbTxrCache = _r$data.slbTxrCache,
28584 tlbTxrCache = _r$data.tlbTxrCache;
28585 var bb = ele.boundingBox();
28586 var reason = requestHighQuality === true ? eleTxrCache.reasons.highQuality : null;
28587
28588 if (bb.w === 0 || bb.h === 0 || !ele.visible()) {
28589 return;
28590 }
28591
28592 if (!extent || boundingBoxesIntersect(bb, extent)) {
28593 var isEdge = ele.isEdge();
28594
28595 var badLine = ele.element()._private.rscratch.badLine;
28596
28597 r.drawCachedElementPortion(context, ele, eleTxrCache, pxRatio, lvl, reason, getZeroRotation, getOpacity);
28598
28599 if (!isEdge || !badLine) {
28600 r.drawCachedElementPortion(context, ele, lblTxrCache, pxRatio, lvl, reason, getLabelRotation, getTextOpacity);
28601 }
28602
28603 if (isEdge && !badLine) {
28604 r.drawCachedElementPortion(context, ele, slbTxrCache, pxRatio, lvl, reason, getSourceLabelRotation, getTextOpacity);
28605 r.drawCachedElementPortion(context, ele, tlbTxrCache, pxRatio, lvl, reason, getTargetLabelRotation, getTextOpacity);
28606 }
28607
28608 r.drawElementOverlay(context, ele);
28609 }
28610};
28611
28612CRp$1.drawElements = function (context, eles) {
28613 var r = this;
28614
28615 for (var i = 0; i < eles.length; i++) {
28616 var ele = eles[i];
28617 r.drawElement(context, ele);
28618 }
28619};
28620
28621CRp$1.drawCachedElements = function (context, eles, pxRatio, extent) {
28622 var r = this;
28623
28624 for (var i = 0; i < eles.length; i++) {
28625 var ele = eles[i];
28626 r.drawCachedElement(context, ele, pxRatio, extent);
28627 }
28628};
28629
28630CRp$1.drawCachedNodes = function (context, eles, pxRatio, extent) {
28631 var r = this;
28632
28633 for (var i = 0; i < eles.length; i++) {
28634 var ele = eles[i];
28635
28636 if (!ele.isNode()) {
28637 continue;
28638 }
28639
28640 r.drawCachedElement(context, ele, pxRatio, extent);
28641 }
28642};
28643
28644CRp$1.drawLayeredElements = function (context, eles, pxRatio, extent) {
28645 var r = this;
28646 var layers = r.data.lyrTxrCache.getLayers(eles, pxRatio);
28647
28648 if (layers) {
28649 for (var i = 0; i < layers.length; i++) {
28650 var layer = layers[i];
28651 var bb = layer.bb;
28652
28653 if (bb.w === 0 || bb.h === 0) {
28654 continue;
28655 }
28656
28657 context.drawImage(layer.canvas, bb.x1, bb.y1, bb.w, bb.h);
28658 }
28659 } else {
28660 // fall back on plain caching if no layers
28661 r.drawCachedElements(context, eles, pxRatio, extent);
28662 }
28663};
28664
28665/* global Path2D */
28666var CRp$2 = {};
28667
28668CRp$2.drawEdge = function (context, edge, shiftToOriginWithBb) {
28669 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
28670 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
28671 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
28672 var r = this;
28673 var rs = edge._private.rscratch;
28674
28675 if (shouldDrawOpacity && !edge.visible()) {
28676 return;
28677 } // if bezier ctrl pts can not be calculated, then die
28678
28679
28680 if (rs.badLine || rs.allpts == null || isNaN(rs.allpts[0])) {
28681 // isNaN in case edge is impossible and browser bugs (e.g. safari)
28682 return;
28683 }
28684
28685 var bb;
28686
28687 if (shiftToOriginWithBb) {
28688 bb = shiftToOriginWithBb;
28689 context.translate(-bb.x1, -bb.y1);
28690 }
28691
28692 var opacity = shouldDrawOpacity ? edge.pstyle('opacity').value : 1;
28693 var lineOpacity = shouldDrawOpacity ? edge.pstyle('line-opacity').value : 1;
28694 var lineStyle = edge.pstyle('line-style').value;
28695 var edgeWidth = edge.pstyle('width').pfValue;
28696 var lineCap = edge.pstyle('line-cap').value;
28697 var effectiveLineOpacity = opacity * lineOpacity; // separate arrow opacity would require arrow-opacity property
28698
28699 var effectiveArrowOpacity = opacity * lineOpacity;
28700
28701 var drawLine = function drawLine() {
28702 var strokeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveLineOpacity;
28703 context.lineWidth = edgeWidth;
28704 context.lineCap = lineCap;
28705 r.eleStrokeStyle(context, edge, strokeOpacity);
28706 r.drawEdgePath(edge, context, rs.allpts, lineStyle);
28707 context.lineCap = 'butt'; // reset for other drawing functions
28708 };
28709
28710 var drawOverlay = function drawOverlay() {
28711 if (!shouldDrawOverlay) {
28712 return;
28713 }
28714
28715 r.drawEdgeOverlay(context, edge);
28716 };
28717
28718 var drawArrows = function drawArrows() {
28719 var arrowOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : effectiveArrowOpacity;
28720 r.drawArrowheads(context, edge, arrowOpacity);
28721 };
28722
28723 var drawText = function drawText() {
28724 r.drawElementText(context, edge, null, drawLabel);
28725 };
28726
28727 context.lineJoin = 'round';
28728 var ghost = edge.pstyle('ghost').value === 'yes';
28729
28730 if (ghost) {
28731 var gx = edge.pstyle('ghost-offset-x').pfValue;
28732 var gy = edge.pstyle('ghost-offset-y').pfValue;
28733 var ghostOpacity = edge.pstyle('ghost-opacity').value;
28734 var effectiveGhostOpacity = effectiveLineOpacity * ghostOpacity;
28735 context.translate(gx, gy);
28736 drawLine(effectiveGhostOpacity);
28737 drawArrows(effectiveGhostOpacity);
28738 context.translate(-gx, -gy);
28739 }
28740
28741 drawLine();
28742 drawArrows();
28743 drawOverlay();
28744 drawText();
28745
28746 if (shiftToOriginWithBb) {
28747 context.translate(bb.x1, bb.y1);
28748 }
28749};
28750
28751CRp$2.drawEdgeOverlay = function (context, edge) {
28752 if (!edge.visible()) {
28753 return;
28754 }
28755
28756 var overlayOpacity = edge.pstyle('overlay-opacity').value;
28757
28758 if (overlayOpacity === 0) {
28759 return;
28760 }
28761
28762 var r = this;
28763 var usePaths = r.usePaths();
28764 var rs = edge._private.rscratch;
28765 var overlayPadding = edge.pstyle('overlay-padding').pfValue;
28766 var overlayWidth = 2 * overlayPadding;
28767 var overlayColor = edge.pstyle('overlay-color').value;
28768 context.lineWidth = overlayWidth;
28769
28770 if (rs.edgeType === 'self' && !usePaths) {
28771 context.lineCap = 'butt';
28772 } else {
28773 context.lineCap = 'round';
28774 }
28775
28776 r.colorStrokeStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
28777 r.drawEdgePath(edge, context, rs.allpts, 'solid');
28778};
28779
28780CRp$2.drawEdgePath = function (edge, context, pts, type) {
28781 var rs = edge._private.rscratch;
28782 var canvasCxt = context;
28783 var path;
28784 var pathCacheHit = false;
28785 var usePaths = this.usePaths();
28786 var lineDashPattern = edge.pstyle('line-dash-pattern').pfValue;
28787 var lineDashOffset = edge.pstyle('line-dash-offset').pfValue;
28788
28789 if (usePaths) {
28790 var pathCacheKey = pts.join('$');
28791 var keyMatches = rs.pathCacheKey && rs.pathCacheKey === pathCacheKey;
28792
28793 if (keyMatches) {
28794 path = context = rs.pathCache;
28795 pathCacheHit = true;
28796 } else {
28797 path = context = new Path2D();
28798 rs.pathCacheKey = pathCacheKey;
28799 rs.pathCache = path;
28800 }
28801 }
28802
28803 if (canvasCxt.setLineDash) {
28804 // for very outofdate browsers
28805 switch (type) {
28806 case 'dotted':
28807 canvasCxt.setLineDash([1, 1]);
28808 break;
28809
28810 case 'dashed':
28811 canvasCxt.setLineDash(lineDashPattern);
28812 canvasCxt.lineDashOffset = lineDashOffset;
28813 break;
28814
28815 case 'solid':
28816 canvasCxt.setLineDash([]);
28817 break;
28818 }
28819 }
28820
28821 if (!pathCacheHit && !rs.badLine) {
28822 if (context.beginPath) {
28823 context.beginPath();
28824 }
28825
28826 context.moveTo(pts[0], pts[1]);
28827
28828 switch (rs.edgeType) {
28829 case 'bezier':
28830 case 'self':
28831 case 'compound':
28832 case 'multibezier':
28833 for (var i = 2; i + 3 < pts.length; i += 4) {
28834 context.quadraticCurveTo(pts[i], pts[i + 1], pts[i + 2], pts[i + 3]);
28835 }
28836
28837 break;
28838
28839 case 'straight':
28840 case 'segments':
28841 case 'haystack':
28842 for (var _i = 2; _i + 1 < pts.length; _i += 2) {
28843 context.lineTo(pts[_i], pts[_i + 1]);
28844 }
28845
28846 break;
28847 }
28848 }
28849
28850 context = canvasCxt;
28851
28852 if (usePaths) {
28853 context.stroke(path);
28854 } else {
28855 context.stroke();
28856 } // reset any line dashes
28857
28858
28859 if (context.setLineDash) {
28860 // for very outofdate browsers
28861 context.setLineDash([]);
28862 }
28863};
28864
28865CRp$2.drawArrowheads = function (context, edge, opacity) {
28866 var rs = edge._private.rscratch;
28867 var isHaystack = rs.edgeType === 'haystack';
28868
28869 if (!isHaystack) {
28870 this.drawArrowhead(context, edge, 'source', rs.arrowStartX, rs.arrowStartY, rs.srcArrowAngle, opacity);
28871 }
28872
28873 this.drawArrowhead(context, edge, 'mid-target', rs.midX, rs.midY, rs.midtgtArrowAngle, opacity);
28874 this.drawArrowhead(context, edge, 'mid-source', rs.midX, rs.midY, rs.midsrcArrowAngle, opacity);
28875
28876 if (!isHaystack) {
28877 this.drawArrowhead(context, edge, 'target', rs.arrowEndX, rs.arrowEndY, rs.tgtArrowAngle, opacity);
28878 }
28879};
28880
28881CRp$2.drawArrowhead = function (context, edge, prefix, x, y, angle, opacity) {
28882 if (isNaN(x) || x == null || isNaN(y) || y == null || isNaN(angle) || angle == null) {
28883 return;
28884 }
28885
28886 var self = this;
28887 var arrowShape = edge.pstyle(prefix + '-arrow-shape').value;
28888
28889 if (arrowShape === 'none') {
28890 return;
28891 }
28892
28893 var arrowClearFill = edge.pstyle(prefix + '-arrow-fill').value === 'hollow' ? 'both' : 'filled';
28894 var arrowFill = edge.pstyle(prefix + '-arrow-fill').value;
28895 var edgeWidth = edge.pstyle('width').pfValue;
28896 var edgeOpacity = edge.pstyle('opacity').value;
28897
28898 if (opacity === undefined) {
28899 opacity = edgeOpacity;
28900 }
28901
28902 var gco = context.globalCompositeOperation;
28903
28904 if (opacity !== 1 || arrowFill === 'hollow') {
28905 // then extra clear is needed
28906 context.globalCompositeOperation = 'destination-out';
28907 self.colorFillStyle(context, 255, 255, 255, 1);
28908 self.colorStrokeStyle(context, 255, 255, 255, 1);
28909 self.drawArrowShape(edge, context, arrowClearFill, edgeWidth, arrowShape, x, y, angle);
28910 context.globalCompositeOperation = gco;
28911 } // otherwise, the opaque arrow clears it for free :)
28912
28913
28914 var color = edge.pstyle(prefix + '-arrow-color').value;
28915 self.colorFillStyle(context, color[0], color[1], color[2], opacity);
28916 self.colorStrokeStyle(context, color[0], color[1], color[2], opacity);
28917 self.drawArrowShape(edge, context, arrowFill, edgeWidth, arrowShape, x, y, angle);
28918};
28919
28920CRp$2.drawArrowShape = function (edge, context, fill, edgeWidth, shape, x, y, angle) {
28921 var r = this;
28922 var usePaths = this.usePaths() && shape !== 'triangle-cross';
28923 var pathCacheHit = false;
28924 var path;
28925 var canvasContext = context;
28926 var translation = {
28927 x: x,
28928 y: y
28929 };
28930 var scale = edge.pstyle('arrow-scale').value;
28931 var size = this.getArrowWidth(edgeWidth, scale);
28932 var shapeImpl = r.arrowShapes[shape];
28933
28934 if (usePaths) {
28935 var cache = r.arrowPathCache = r.arrowPathCache || [];
28936 var key = hashString(shape);
28937 var cachedPath = cache[key];
28938
28939 if (cachedPath != null) {
28940 path = context = cachedPath;
28941 pathCacheHit = true;
28942 } else {
28943 path = context = new Path2D();
28944 cache[key] = path;
28945 }
28946 }
28947
28948 if (!pathCacheHit) {
28949 if (context.beginPath) {
28950 context.beginPath();
28951 }
28952
28953 if (usePaths) {
28954 // store in the path cache with values easily manipulated later
28955 shapeImpl.draw(context, 1, 0, {
28956 x: 0,
28957 y: 0
28958 }, 1);
28959 } else {
28960 shapeImpl.draw(context, size, angle, translation, edgeWidth);
28961 }
28962
28963 if (context.closePath) {
28964 context.closePath();
28965 }
28966 }
28967
28968 context = canvasContext;
28969
28970 if (usePaths) {
28971 // set transform to arrow position/orientation
28972 context.translate(x, y);
28973 context.rotate(angle);
28974 context.scale(size, size);
28975 }
28976
28977 if (fill === 'filled' || fill === 'both') {
28978 if (usePaths) {
28979 context.fill(path);
28980 } else {
28981 context.fill();
28982 }
28983 }
28984
28985 if (fill === 'hollow' || fill === 'both') {
28986 context.lineWidth = (shapeImpl.matchEdgeWidth ? edgeWidth : 1) / (usePaths ? size : 1);
28987 context.lineJoin = 'miter';
28988
28989 if (usePaths) {
28990 context.stroke(path);
28991 } else {
28992 context.stroke();
28993 }
28994 }
28995
28996 if (usePaths) {
28997 // reset transform by applying inverse
28998 context.scale(1 / size, 1 / size);
28999 context.rotate(-angle);
29000 context.translate(-x, -y);
29001 }
29002};
29003
29004var CRp$3 = {};
29005
29006CRp$3.safeDrawImage = function (context, img, ix, iy, iw, ih, x, y, w, h) {
29007 // detect problematic cases for old browsers with bad images (cheaper than try-catch)
29008 if (iw <= 0 || ih <= 0 || w <= 0 || h <= 0) {
29009 return;
29010 }
29011
29012 context.drawImage(img, ix, iy, iw, ih, x, y, w, h);
29013};
29014
29015CRp$3.drawInscribedImage = function (context, img, node, index, nodeOpacity) {
29016 var r = this;
29017 var pos = node.position();
29018 var nodeX = pos.x;
29019 var nodeY = pos.y;
29020 var styleObj = node.cy().style();
29021 var getIndexedStyle = styleObj.getIndexedStyle.bind(styleObj);
29022 var fit = getIndexedStyle(node, 'background-fit', 'value', index);
29023 var repeat = getIndexedStyle(node, 'background-repeat', 'value', index);
29024 var nodeW = node.width();
29025 var nodeH = node.height();
29026 var paddingX2 = node.padding() * 2;
29027 var nodeTW = nodeW + (getIndexedStyle(node, 'background-width-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29028 var nodeTH = nodeH + (getIndexedStyle(node, 'background-height-relative-to', 'value', index) === 'inner' ? 0 : paddingX2);
29029 var rs = node._private.rscratch;
29030 var clip = getIndexedStyle(node, 'background-clip', 'value', index);
29031 var shouldClip = clip === 'node';
29032 var imgOpacity = getIndexedStyle(node, 'background-image-opacity', 'value', index) * nodeOpacity;
29033 var smooth = getIndexedStyle(node, 'background-image-smoothing', 'value', index);
29034 var imgW = img.width || img.cachedW;
29035 var imgH = img.height || img.cachedH; // workaround for broken browsers like ie
29036
29037 if (null == imgW || null == imgH) {
29038 document.body.appendChild(img); // eslint-disable-line no-undef
29039
29040 imgW = img.cachedW = img.width || img.offsetWidth;
29041 imgH = img.cachedH = img.height || img.offsetHeight;
29042 document.body.removeChild(img); // eslint-disable-line no-undef
29043 }
29044
29045 var w = imgW;
29046 var h = imgH;
29047
29048 if (getIndexedStyle(node, 'background-width', 'value', index) !== 'auto') {
29049 if (getIndexedStyle(node, 'background-width', 'units', index) === '%') {
29050 w = getIndexedStyle(node, 'background-width', 'pfValue', index) * nodeTW;
29051 } else {
29052 w = getIndexedStyle(node, 'background-width', 'pfValue', index);
29053 }
29054 }
29055
29056 if (getIndexedStyle(node, 'background-height', 'value', index) !== 'auto') {
29057 if (getIndexedStyle(node, 'background-height', 'units', index) === '%') {
29058 h = getIndexedStyle(node, 'background-height', 'pfValue', index) * nodeTH;
29059 } else {
29060 h = getIndexedStyle(node, 'background-height', 'pfValue', index);
29061 }
29062 }
29063
29064 if (w === 0 || h === 0) {
29065 return; // no point in drawing empty image (and chrome is broken in this case)
29066 }
29067
29068 if (fit === 'contain') {
29069 var scale = Math.min(nodeTW / w, nodeTH / h);
29070 w *= scale;
29071 h *= scale;
29072 } else if (fit === 'cover') {
29073 var scale = Math.max(nodeTW / w, nodeTH / h);
29074 w *= scale;
29075 h *= scale;
29076 }
29077
29078 var x = nodeX - nodeTW / 2; // left
29079
29080 var posXUnits = getIndexedStyle(node, 'background-position-x', 'units', index);
29081 var posXPfVal = getIndexedStyle(node, 'background-position-x', 'pfValue', index);
29082
29083 if (posXUnits === '%') {
29084 x += (nodeTW - w) * posXPfVal;
29085 } else {
29086 x += posXPfVal;
29087 }
29088
29089 var offXUnits = getIndexedStyle(node, 'background-offset-x', 'units', index);
29090 var offXPfVal = getIndexedStyle(node, 'background-offset-x', 'pfValue', index);
29091
29092 if (offXUnits === '%') {
29093 x += (nodeTW - w) * offXPfVal;
29094 } else {
29095 x += offXPfVal;
29096 }
29097
29098 var y = nodeY - nodeTH / 2; // top
29099
29100 var posYUnits = getIndexedStyle(node, 'background-position-y', 'units', index);
29101 var posYPfVal = getIndexedStyle(node, 'background-position-y', 'pfValue', index);
29102
29103 if (posYUnits === '%') {
29104 y += (nodeTH - h) * posYPfVal;
29105 } else {
29106 y += posYPfVal;
29107 }
29108
29109 var offYUnits = getIndexedStyle(node, 'background-offset-y', 'units', index);
29110 var offYPfVal = getIndexedStyle(node, 'background-offset-y', 'pfValue', index);
29111
29112 if (offYUnits === '%') {
29113 y += (nodeTH - h) * offYPfVal;
29114 } else {
29115 y += offYPfVal;
29116 }
29117
29118 if (rs.pathCache) {
29119 x -= nodeX;
29120 y -= nodeY;
29121 nodeX = 0;
29122 nodeY = 0;
29123 }
29124
29125 var gAlpha = context.globalAlpha;
29126 context.globalAlpha = imgOpacity;
29127 var smoothingEnabled = r.getImgSmoothing(context);
29128 var isSmoothingSwitched = false;
29129
29130 if (smooth === 'no' && smoothingEnabled) {
29131 r.setImgSmoothing(context, false);
29132 isSmoothingSwitched = true;
29133 } else if (smooth === 'yes' && !smoothingEnabled) {
29134 r.setImgSmoothing(context, true);
29135 isSmoothingSwitched = true;
29136 }
29137
29138 if (repeat === 'no-repeat') {
29139 if (shouldClip) {
29140 context.save();
29141
29142 if (rs.pathCache) {
29143 context.clip(rs.pathCache);
29144 } else {
29145 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29146 context.clip();
29147 }
29148 }
29149
29150 r.safeDrawImage(context, img, 0, 0, imgW, imgH, x, y, w, h);
29151
29152 if (shouldClip) {
29153 context.restore();
29154 }
29155 } else {
29156 var pattern = context.createPattern(img, repeat);
29157 context.fillStyle = pattern;
29158 r.nodeShapes[r.getNodeShape(node)].draw(context, nodeX, nodeY, nodeTW, nodeTH);
29159 context.translate(x, y);
29160 context.fill();
29161 context.translate(-x, -y);
29162 }
29163
29164 context.globalAlpha = gAlpha;
29165
29166 if (isSmoothingSwitched) {
29167 r.setImgSmoothing(context, smoothingEnabled);
29168 }
29169};
29170
29171var CRp$4 = {};
29172
29173CRp$4.eleTextBiggerThanMin = function (ele, scale) {
29174 if (!scale) {
29175 var zoom = ele.cy().zoom();
29176 var pxRatio = this.getPixelRatio();
29177 var lvl = Math.ceil(log2(zoom * pxRatio)); // the effective texture level
29178
29179 scale = Math.pow(2, lvl);
29180 }
29181
29182 var computedSize = ele.pstyle('font-size').pfValue * scale;
29183 var minSize = ele.pstyle('min-zoomed-font-size').pfValue;
29184
29185 if (computedSize < minSize) {
29186 return false;
29187 }
29188
29189 return true;
29190};
29191
29192CRp$4.drawElementText = function (context, ele, shiftToOriginWithBb, force, prefix) {
29193 var useEleOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29194 var r = this;
29195
29196 if (force == null) {
29197 if (useEleOpacity && !r.eleTextBiggerThanMin(ele)) {
29198 return;
29199 }
29200 } else if (force === false) {
29201 return;
29202 }
29203
29204 if (ele.isNode()) {
29205 var label = ele.pstyle('label');
29206
29207 if (!label || !label.value) {
29208 return;
29209 }
29210
29211 var justification = r.getLabelJustification(ele);
29212 context.textAlign = justification;
29213 context.textBaseline = 'bottom';
29214 } else {
29215 var badLine = ele.element()._private.rscratch.badLine;
29216
29217 var _label = ele.pstyle('label');
29218
29219 var srcLabel = ele.pstyle('source-label');
29220 var tgtLabel = ele.pstyle('target-label');
29221
29222 if (badLine || (!_label || !_label.value) && (!srcLabel || !srcLabel.value) && (!tgtLabel || !tgtLabel.value)) {
29223 return;
29224 }
29225
29226 context.textAlign = 'center';
29227 context.textBaseline = 'bottom';
29228 }
29229
29230 var applyRotation = !shiftToOriginWithBb;
29231 var bb;
29232
29233 if (shiftToOriginWithBb) {
29234 bb = shiftToOriginWithBb;
29235 context.translate(-bb.x1, -bb.y1);
29236 }
29237
29238 if (prefix == null) {
29239 r.drawText(context, ele, null, applyRotation, useEleOpacity);
29240
29241 if (ele.isEdge()) {
29242 r.drawText(context, ele, 'source', applyRotation, useEleOpacity);
29243 r.drawText(context, ele, 'target', applyRotation, useEleOpacity);
29244 }
29245 } else {
29246 r.drawText(context, ele, prefix, applyRotation, useEleOpacity);
29247 }
29248
29249 if (shiftToOriginWithBb) {
29250 context.translate(bb.x1, bb.y1);
29251 }
29252};
29253
29254CRp$4.getFontCache = function (context) {
29255 var cache;
29256 this.fontCaches = this.fontCaches || [];
29257
29258 for (var i = 0; i < this.fontCaches.length; i++) {
29259 cache = this.fontCaches[i];
29260
29261 if (cache.context === context) {
29262 return cache;
29263 }
29264 }
29265
29266 cache = {
29267 context: context
29268 };
29269 this.fontCaches.push(cache);
29270 return cache;
29271}; // set up canvas context with font
29272// returns transformed text string
29273
29274
29275CRp$4.setupTextStyle = function (context, ele) {
29276 var useEleOpacity = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
29277 // Font style
29278 var labelStyle = ele.pstyle('font-style').strValue;
29279 var labelSize = ele.pstyle('font-size').pfValue + 'px';
29280 var labelFamily = ele.pstyle('font-family').strValue;
29281 var labelWeight = ele.pstyle('font-weight').strValue;
29282 var opacity = useEleOpacity ? ele.effectiveOpacity() * ele.pstyle('text-opacity').value : 1;
29283 var outlineOpacity = ele.pstyle('text-outline-opacity').value * opacity;
29284 var color = ele.pstyle('color').value;
29285 var outlineColor = ele.pstyle('text-outline-color').value;
29286 context.font = labelStyle + ' ' + labelWeight + ' ' + labelSize + ' ' + labelFamily;
29287 context.lineJoin = 'round'; // so text outlines aren't jagged
29288
29289 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29290 this.colorStrokeStyle(context, outlineColor[0], outlineColor[1], outlineColor[2], outlineOpacity);
29291}; // TODO ensure re-used
29292
29293
29294function roundRect(ctx, x, y, width, height) {
29295 var radius = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : 5;
29296 ctx.beginPath();
29297 ctx.moveTo(x + radius, y);
29298 ctx.lineTo(x + width - radius, y);
29299 ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
29300 ctx.lineTo(x + width, y + height - radius);
29301 ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
29302 ctx.lineTo(x + radius, y + height);
29303 ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
29304 ctx.lineTo(x, y + radius);
29305 ctx.quadraticCurveTo(x, y, x + radius, y);
29306 ctx.closePath();
29307 ctx.fill();
29308}
29309
29310CRp$4.getTextAngle = function (ele, prefix) {
29311 var theta;
29312 var _p = ele._private;
29313 var rscratch = _p.rscratch;
29314 var pdash = prefix ? prefix + '-' : '';
29315 var rotation = ele.pstyle(pdash + 'text-rotation');
29316 var textAngle = getPrefixedProperty(rscratch, 'labelAngle', prefix);
29317
29318 if (rotation.strValue === 'autorotate') {
29319 theta = ele.isEdge() ? textAngle : 0;
29320 } else if (rotation.strValue === 'none') {
29321 theta = 0;
29322 } else {
29323 theta = rotation.pfValue;
29324 }
29325
29326 return theta;
29327};
29328
29329CRp$4.drawText = function (context, ele, prefix) {
29330 var applyRotation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29331 var useEleOpacity = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29332 var _p = ele._private;
29333 var rscratch = _p.rscratch;
29334 var parentOpacity = useEleOpacity ? ele.effectiveOpacity() : 1;
29335
29336 if (useEleOpacity && (parentOpacity === 0 || ele.pstyle('text-opacity').value === 0)) {
29337 return;
29338 } // use 'main' as an alias for the main label (i.e. null prefix)
29339
29340
29341 if (prefix === 'main') {
29342 prefix = null;
29343 }
29344
29345 var textX = getPrefixedProperty(rscratch, 'labelX', prefix);
29346 var textY = getPrefixedProperty(rscratch, 'labelY', prefix);
29347 var orgTextX, orgTextY; // used for rotation
29348
29349 var text = this.getLabelText(ele, prefix);
29350
29351 if (text != null && text !== '' && !isNaN(textX) && !isNaN(textY)) {
29352 this.setupTextStyle(context, ele, useEleOpacity);
29353 var pdash = prefix ? prefix + '-' : '';
29354 var textW = getPrefixedProperty(rscratch, 'labelWidth', prefix);
29355 var textH = getPrefixedProperty(rscratch, 'labelHeight', prefix);
29356 var marginX = ele.pstyle(pdash + 'text-margin-x').pfValue;
29357 var marginY = ele.pstyle(pdash + 'text-margin-y').pfValue;
29358 var isEdge = ele.isEdge();
29359 var halign = ele.pstyle('text-halign').value;
29360 var valign = ele.pstyle('text-valign').value;
29361
29362 if (isEdge) {
29363 halign = 'center';
29364 valign = 'center';
29365 }
29366
29367 textX += marginX;
29368 textY += marginY;
29369 var theta;
29370
29371 if (!applyRotation) {
29372 theta = 0;
29373 } else {
29374 theta = this.getTextAngle(ele, prefix);
29375 }
29376
29377 if (theta !== 0) {
29378 orgTextX = textX;
29379 orgTextY = textY;
29380 context.translate(orgTextX, orgTextY);
29381 context.rotate(theta);
29382 textX = 0;
29383 textY = 0;
29384 }
29385
29386 switch (valign) {
29387 case 'top':
29388 break;
29389
29390 case 'center':
29391 textY += textH / 2;
29392 break;
29393
29394 case 'bottom':
29395 textY += textH;
29396 break;
29397 }
29398
29399 var backgroundOpacity = ele.pstyle('text-background-opacity').value;
29400 var borderOpacity = ele.pstyle('text-border-opacity').value;
29401 var textBorderWidth = ele.pstyle('text-border-width').pfValue;
29402 var backgroundPadding = ele.pstyle('text-background-padding').pfValue;
29403
29404 if (backgroundOpacity > 0 || textBorderWidth > 0 && borderOpacity > 0) {
29405 var bgX = textX - backgroundPadding;
29406
29407 switch (halign) {
29408 case 'left':
29409 bgX -= textW;
29410 break;
29411
29412 case 'center':
29413 bgX -= textW / 2;
29414 break;
29415 }
29416
29417 var bgY = textY - textH - backgroundPadding;
29418 var bgW = textW + 2 * backgroundPadding;
29419 var bgH = textH + 2 * backgroundPadding;
29420
29421 if (backgroundOpacity > 0) {
29422 var textFill = context.fillStyle;
29423 var textBackgroundColor = ele.pstyle('text-background-color').value;
29424 context.fillStyle = 'rgba(' + textBackgroundColor[0] + ',' + textBackgroundColor[1] + ',' + textBackgroundColor[2] + ',' + backgroundOpacity * parentOpacity + ')';
29425 var styleShape = ele.pstyle('text-background-shape').strValue;
29426
29427 if (styleShape.indexOf('round') === 0) {
29428 roundRect(context, bgX, bgY, bgW, bgH, 2);
29429 } else {
29430 context.fillRect(bgX, bgY, bgW, bgH);
29431 }
29432
29433 context.fillStyle = textFill;
29434 }
29435
29436 if (textBorderWidth > 0 && borderOpacity > 0) {
29437 var textStroke = context.strokeStyle;
29438 var textLineWidth = context.lineWidth;
29439 var textBorderColor = ele.pstyle('text-border-color').value;
29440 var textBorderStyle = ele.pstyle('text-border-style').value;
29441 context.strokeStyle = 'rgba(' + textBorderColor[0] + ',' + textBorderColor[1] + ',' + textBorderColor[2] + ',' + borderOpacity * parentOpacity + ')';
29442 context.lineWidth = textBorderWidth;
29443
29444 if (context.setLineDash) {
29445 // for very outofdate browsers
29446 switch (textBorderStyle) {
29447 case 'dotted':
29448 context.setLineDash([1, 1]);
29449 break;
29450
29451 case 'dashed':
29452 context.setLineDash([4, 2]);
29453 break;
29454
29455 case 'double':
29456 context.lineWidth = textBorderWidth / 4; // 50% reserved for white between the two borders
29457
29458 context.setLineDash([]);
29459 break;
29460
29461 case 'solid':
29462 context.setLineDash([]);
29463 break;
29464 }
29465 }
29466
29467 context.strokeRect(bgX, bgY, bgW, bgH);
29468
29469 if (textBorderStyle === 'double') {
29470 var whiteWidth = textBorderWidth / 2;
29471 context.strokeRect(bgX + whiteWidth, bgY + whiteWidth, bgW - whiteWidth * 2, bgH - whiteWidth * 2);
29472 }
29473
29474 if (context.setLineDash) {
29475 // for very outofdate browsers
29476 context.setLineDash([]);
29477 }
29478
29479 context.lineWidth = textLineWidth;
29480 context.strokeStyle = textStroke;
29481 }
29482 }
29483
29484 var lineWidth = 2 * ele.pstyle('text-outline-width').pfValue; // *2 b/c the stroke is drawn centred on the middle
29485
29486 if (lineWidth > 0) {
29487 context.lineWidth = lineWidth;
29488 }
29489
29490 if (ele.pstyle('text-wrap').value === 'wrap') {
29491 var lines = getPrefixedProperty(rscratch, 'labelWrapCachedLines', prefix);
29492 var lineHeight = getPrefixedProperty(rscratch, 'labelLineHeight', prefix);
29493 var halfTextW = textW / 2;
29494 var justification = this.getLabelJustification(ele);
29495
29496 if (justification === 'auto') ; else if (halign === 'left') {
29497 // auto justification : right
29498 if (justification === 'left') {
29499 textX += -textW;
29500 } else if (justification === 'center') {
29501 textX += -halfTextW;
29502 } // else same as auto
29503
29504 } else if (halign === 'center') {
29505 // auto justfication : center
29506 if (justification === 'left') {
29507 textX += -halfTextW;
29508 } else if (justification === 'right') {
29509 textX += halfTextW;
29510 } // else same as auto
29511
29512 } else if (halign === 'right') {
29513 // auto justification : left
29514 if (justification === 'center') {
29515 textX += halfTextW;
29516 } else if (justification === 'right') {
29517 textX += textW;
29518 } // else same as auto
29519
29520 }
29521
29522 switch (valign) {
29523 case 'top':
29524 textY -= (lines.length - 1) * lineHeight;
29525 break;
29526
29527 case 'center':
29528 case 'bottom':
29529 textY -= (lines.length - 1) * lineHeight;
29530 break;
29531 }
29532
29533 for (var l = 0; l < lines.length; l++) {
29534 if (lineWidth > 0) {
29535 context.strokeText(lines[l], textX, textY);
29536 }
29537
29538 context.fillText(lines[l], textX, textY);
29539 textY += lineHeight;
29540 }
29541 } else {
29542 if (lineWidth > 0) {
29543 context.strokeText(text, textX, textY);
29544 }
29545
29546 context.fillText(text, textX, textY);
29547 }
29548
29549 if (theta !== 0) {
29550 context.rotate(-theta);
29551 context.translate(-orgTextX, -orgTextY);
29552 }
29553 }
29554};
29555
29556/* global Path2D */
29557var CRp$5 = {};
29558
29559CRp$5.drawNode = function (context, node, shiftToOriginWithBb) {
29560 var drawLabel = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
29561 var shouldDrawOverlay = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
29562 var shouldDrawOpacity = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : true;
29563 var r = this;
29564 var nodeWidth, nodeHeight;
29565 var _p = node._private;
29566 var rs = _p.rscratch;
29567 var pos = node.position();
29568
29569 if (!number(pos.x) || !number(pos.y)) {
29570 return; // can't draw node with undefined position
29571 }
29572
29573 if (shouldDrawOpacity && !node.visible()) {
29574 return;
29575 }
29576
29577 var eleOpacity = shouldDrawOpacity ? node.effectiveOpacity() : 1;
29578 var usePaths = r.usePaths();
29579 var path;
29580 var pathCacheHit = false;
29581 var padding = node.padding();
29582 nodeWidth = node.width() + 2 * padding;
29583 nodeHeight = node.height() + 2 * padding; //
29584 // setup shift
29585
29586 var bb;
29587
29588 if (shiftToOriginWithBb) {
29589 bb = shiftToOriginWithBb;
29590 context.translate(-bb.x1, -bb.y1);
29591 } //
29592 // load bg image
29593
29594
29595 var bgImgProp = node.pstyle('background-image');
29596 var urls = bgImgProp.value;
29597 var urlDefined = new Array(urls.length);
29598 var image = new Array(urls.length);
29599 var numImages = 0;
29600
29601 for (var i = 0; i < urls.length; i++) {
29602 var url = urls[i];
29603 var defd = urlDefined[i] = url != null && url !== 'none';
29604
29605 if (defd) {
29606 var bgImgCrossOrigin = node.cy().style().getIndexedStyle(node, 'background-image-crossorigin', 'value', i);
29607 numImages++; // get image, and if not loaded then ask to redraw when later loaded
29608
29609 image[i] = r.getCachedImage(url, bgImgCrossOrigin, function () {
29610 _p.backgroundTimestamp = Date.now();
29611 node.emitAndNotify('background');
29612 });
29613 }
29614 } //
29615 // setup styles
29616
29617
29618 var darkness = node.pstyle('background-blacken').value;
29619 var borderWidth = node.pstyle('border-width').pfValue;
29620 var bgOpacity = node.pstyle('background-opacity').value * eleOpacity;
29621 var borderColor = node.pstyle('border-color').value;
29622 var borderStyle = node.pstyle('border-style').value;
29623 var borderOpacity = node.pstyle('border-opacity').value * eleOpacity;
29624 context.lineJoin = 'miter'; // so borders are square with the node shape
29625
29626 var setupShapeColor = function setupShapeColor() {
29627 var bgOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : bgOpacity;
29628 r.eleFillStyle(context, node, bgOpy);
29629 };
29630
29631 var setupBorderColor = function setupBorderColor() {
29632 var bdrOpy = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : borderOpacity;
29633 r.colorStrokeStyle(context, borderColor[0], borderColor[1], borderColor[2], bdrOpy);
29634 }; //
29635 // setup shape
29636
29637
29638 var styleShape = node.pstyle('shape').strValue;
29639 var shapePts = node.pstyle('shape-polygon-points').pfValue;
29640
29641 if (usePaths) {
29642 context.translate(pos.x, pos.y);
29643 var pathCache = r.nodePathCache = r.nodePathCache || [];
29644 var key = hashStrings(styleShape === 'polygon' ? styleShape + ',' + shapePts.join(',') : styleShape, '' + nodeHeight, '' + nodeWidth);
29645 var cachedPath = pathCache[key];
29646
29647 if (cachedPath != null) {
29648 path = cachedPath;
29649 pathCacheHit = true;
29650 rs.pathCache = path;
29651 } else {
29652 path = new Path2D();
29653 pathCache[key] = rs.pathCache = path;
29654 }
29655 }
29656
29657 var drawShape = function drawShape() {
29658 if (!pathCacheHit) {
29659 var npos = pos;
29660
29661 if (usePaths) {
29662 npos = {
29663 x: 0,
29664 y: 0
29665 };
29666 }
29667
29668 r.nodeShapes[r.getNodeShape(node)].draw(path || context, npos.x, npos.y, nodeWidth, nodeHeight);
29669 }
29670
29671 if (usePaths) {
29672 context.fill(path);
29673 } else {
29674 context.fill();
29675 }
29676 };
29677
29678 var drawImages = function drawImages() {
29679 var nodeOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29680 var inside = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
29681 var prevBging = _p.backgrounding;
29682 var totalCompleted = 0;
29683
29684 for (var _i = 0; _i < image.length; _i++) {
29685 var bgContainment = node.cy().style().getIndexedStyle(node, 'background-image-containment', 'value', _i);
29686
29687 if (inside && bgContainment === 'over' || !inside && bgContainment === 'inside') {
29688 totalCompleted++;
29689 continue;
29690 }
29691
29692 if (urlDefined[_i] && image[_i].complete && !image[_i].error) {
29693 totalCompleted++;
29694 r.drawInscribedImage(context, image[_i], node, _i, nodeOpacity);
29695 }
29696 }
29697
29698 _p.backgrounding = !(totalCompleted === numImages);
29699
29700 if (prevBging !== _p.backgrounding) {
29701 // update style b/c :backgrounding state changed
29702 node.updateStyle(false);
29703 }
29704 };
29705
29706 var drawPie = function drawPie() {
29707 var redrawShape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
29708 var pieOpacity = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : eleOpacity;
29709
29710 if (r.hasPie(node)) {
29711 r.drawPie(context, node, pieOpacity); // redraw/restore path if steps after pie need it
29712
29713 if (redrawShape) {
29714 if (!usePaths) {
29715 r.nodeShapes[r.getNodeShape(node)].draw(context, pos.x, pos.y, nodeWidth, nodeHeight);
29716 }
29717 }
29718 }
29719 };
29720
29721 var darken = function darken() {
29722 var darkenOpacity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : eleOpacity;
29723 var opacity = (darkness > 0 ? darkness : -darkness) * darkenOpacity;
29724 var c = darkness > 0 ? 0 : 255;
29725
29726 if (darkness !== 0) {
29727 r.colorFillStyle(context, c, c, c, opacity);
29728
29729 if (usePaths) {
29730 context.fill(path);
29731 } else {
29732 context.fill();
29733 }
29734 }
29735 };
29736
29737 var drawBorder = function drawBorder() {
29738 if (borderWidth > 0) {
29739 context.lineWidth = borderWidth;
29740 context.lineCap = 'butt';
29741
29742 if (context.setLineDash) {
29743 // for very outofdate browsers
29744 switch (borderStyle) {
29745 case 'dotted':
29746 context.setLineDash([1, 1]);
29747 break;
29748
29749 case 'dashed':
29750 context.setLineDash([4, 2]);
29751 break;
29752
29753 case 'solid':
29754 case 'double':
29755 context.setLineDash([]);
29756 break;
29757 }
29758 }
29759
29760 if (usePaths) {
29761 context.stroke(path);
29762 } else {
29763 context.stroke();
29764 }
29765
29766 if (borderStyle === 'double') {
29767 context.lineWidth = borderWidth / 3;
29768 var gco = context.globalCompositeOperation;
29769 context.globalCompositeOperation = 'destination-out';
29770
29771 if (usePaths) {
29772 context.stroke(path);
29773 } else {
29774 context.stroke();
29775 }
29776
29777 context.globalCompositeOperation = gco;
29778 } // reset in case we changed the border style
29779
29780
29781 if (context.setLineDash) {
29782 // for very outofdate browsers
29783 context.setLineDash([]);
29784 }
29785 }
29786 };
29787
29788 var drawOverlay = function drawOverlay() {
29789 if (shouldDrawOverlay) {
29790 r.drawNodeOverlay(context, node, pos, nodeWidth, nodeHeight);
29791 }
29792 };
29793
29794 var drawText = function drawText() {
29795 r.drawElementText(context, node, null, drawLabel);
29796 };
29797
29798 var ghost = node.pstyle('ghost').value === 'yes';
29799
29800 if (ghost) {
29801 var gx = node.pstyle('ghost-offset-x').pfValue;
29802 var gy = node.pstyle('ghost-offset-y').pfValue;
29803 var ghostOpacity = node.pstyle('ghost-opacity').value;
29804 var effGhostOpacity = ghostOpacity * eleOpacity;
29805 context.translate(gx, gy);
29806 setupShapeColor(ghostOpacity * bgOpacity);
29807 drawShape();
29808 drawImages(effGhostOpacity, true);
29809 setupBorderColor(ghostOpacity * borderOpacity);
29810 drawBorder();
29811 drawPie(darkness !== 0 || borderWidth !== 0);
29812 drawImages(effGhostOpacity, false);
29813 darken(effGhostOpacity);
29814 context.translate(-gx, -gy);
29815 }
29816
29817 setupShapeColor();
29818 drawShape();
29819 drawImages(eleOpacity, true);
29820 setupBorderColor();
29821 drawBorder();
29822 drawPie(darkness !== 0 || borderWidth !== 0);
29823 drawImages(eleOpacity, false);
29824 darken();
29825
29826 if (usePaths) {
29827 context.translate(-pos.x, -pos.y);
29828 }
29829
29830 drawText();
29831 drawOverlay(); //
29832 // clean up shift
29833
29834 if (shiftToOriginWithBb) {
29835 context.translate(bb.x1, bb.y1);
29836 }
29837};
29838
29839CRp$5.drawNodeOverlay = function (context, node, pos, nodeWidth, nodeHeight) {
29840 var r = this;
29841
29842 if (!node.visible()) {
29843 return;
29844 }
29845
29846 var overlayPadding = node.pstyle('overlay-padding').pfValue;
29847 var overlayOpacity = node.pstyle('overlay-opacity').value;
29848 var overlayColor = node.pstyle('overlay-color').value;
29849
29850 if (overlayOpacity > 0) {
29851 pos = pos || node.position();
29852
29853 if (nodeWidth == null || nodeHeight == null) {
29854 var padding = node.padding();
29855 nodeWidth = node.width() + 2 * padding;
29856 nodeHeight = node.height() + 2 * padding;
29857 }
29858
29859 r.colorFillStyle(context, overlayColor[0], overlayColor[1], overlayColor[2], overlayOpacity);
29860 r.nodeShapes['roundrectangle'].draw(context, pos.x, pos.y, nodeWidth + overlayPadding * 2, nodeHeight + overlayPadding * 2);
29861 context.fill();
29862 }
29863}; // does the node have at least one pie piece?
29864
29865
29866CRp$5.hasPie = function (node) {
29867 node = node[0]; // ensure ele ref
29868
29869 return node._private.hasPie;
29870};
29871
29872CRp$5.drawPie = function (context, node, nodeOpacity, pos) {
29873 node = node[0]; // ensure ele ref
29874
29875 pos = pos || node.position();
29876 var cyStyle = node.cy().style();
29877 var pieSize = node.pstyle('pie-size');
29878 var x = pos.x;
29879 var y = pos.y;
29880 var nodeW = node.width();
29881 var nodeH = node.height();
29882 var radius = Math.min(nodeW, nodeH) / 2; // must fit in node
29883
29884 var lastPercent = 0; // what % to continue drawing pie slices from on [0, 1]
29885
29886 var usePaths = this.usePaths();
29887
29888 if (usePaths) {
29889 x = 0;
29890 y = 0;
29891 }
29892
29893 if (pieSize.units === '%') {
29894 radius = radius * pieSize.pfValue;
29895 } else if (pieSize.pfValue !== undefined) {
29896 radius = pieSize.pfValue / 2;
29897 }
29898
29899 for (var i = 1; i <= cyStyle.pieBackgroundN; i++) {
29900 // 1..N
29901 var size = node.pstyle('pie-' + i + '-background-size').value;
29902 var color = node.pstyle('pie-' + i + '-background-color').value;
29903 var opacity = node.pstyle('pie-' + i + '-background-opacity').value * nodeOpacity;
29904 var percent = size / 100; // map integer range [0, 100] to [0, 1]
29905 // percent can't push beyond 1
29906
29907 if (percent + lastPercent > 1) {
29908 percent = 1 - lastPercent;
29909 }
29910
29911 var angleStart = 1.5 * Math.PI + 2 * Math.PI * lastPercent; // start at 12 o'clock and go clockwise
29912
29913 var angleDelta = 2 * Math.PI * percent;
29914 var angleEnd = angleStart + angleDelta; // ignore if
29915 // - zero size
29916 // - we're already beyond the full circle
29917 // - adding the current slice would go beyond the full circle
29918
29919 if (size === 0 || lastPercent >= 1 || lastPercent + percent > 1) {
29920 continue;
29921 }
29922
29923 context.beginPath();
29924 context.moveTo(x, y);
29925 context.arc(x, y, radius, angleStart, angleEnd);
29926 context.closePath();
29927 this.colorFillStyle(context, color[0], color[1], color[2], opacity);
29928 context.fill();
29929 lastPercent += percent;
29930 }
29931};
29932
29933var CRp$6 = {};
29934var motionBlurDelay = 100; // var isFirefox = typeof InstallTrigger !== 'undefined';
29935
29936CRp$6.getPixelRatio = function () {
29937 var context = this.data.contexts[0];
29938
29939 if (this.forcedPixelRatio != null) {
29940 return this.forcedPixelRatio;
29941 }
29942
29943 var backingStore = context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio || context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || context.backingStorePixelRatio || 1;
29944 return (window.devicePixelRatio || 1) / backingStore; // eslint-disable-line no-undef
29945};
29946
29947CRp$6.paintCache = function (context) {
29948 var caches = this.paintCaches = this.paintCaches || [];
29949 var needToCreateCache = true;
29950 var cache;
29951
29952 for (var i = 0; i < caches.length; i++) {
29953 cache = caches[i];
29954
29955 if (cache.context === context) {
29956 needToCreateCache = false;
29957 break;
29958 }
29959 }
29960
29961 if (needToCreateCache) {
29962 cache = {
29963 context: context
29964 };
29965 caches.push(cache);
29966 }
29967
29968 return cache;
29969};
29970
29971CRp$6.createGradientStyleFor = function (context, shapeStyleName, ele, fill, opacity) {
29972 var gradientStyle;
29973 var usePaths = this.usePaths();
29974 var colors = ele.pstyle(shapeStyleName + '-gradient-stop-colors').value,
29975 positions = ele.pstyle(shapeStyleName + '-gradient-stop-positions').pfValue;
29976
29977 if (fill === 'radial-gradient') {
29978 if (ele.isEdge()) {
29979 var start = ele.sourceEndpoint(),
29980 end = ele.targetEndpoint(),
29981 mid = ele.midpoint();
29982 var d1 = dist(start, mid);
29983 var d2 = dist(end, mid);
29984 gradientStyle = context.createRadialGradient(mid.x, mid.y, 0, mid.x, mid.y, Math.max(d1, d2));
29985 } else {
29986 var pos = usePaths ? {
29987 x: 0,
29988 y: 0
29989 } : ele.position(),
29990 width = ele.paddedWidth(),
29991 height = ele.paddedHeight();
29992 gradientStyle = context.createRadialGradient(pos.x, pos.y, 0, pos.x, pos.y, Math.max(width, height));
29993 }
29994 } else {
29995 if (ele.isEdge()) {
29996 var _start = ele.sourceEndpoint(),
29997 _end = ele.targetEndpoint();
29998
29999 gradientStyle = context.createLinearGradient(_start.x, _start.y, _end.x, _end.y);
30000 } else {
30001 var _pos = usePaths ? {
30002 x: 0,
30003 y: 0
30004 } : ele.position(),
30005 _width = ele.paddedWidth(),
30006 _height = ele.paddedHeight(),
30007 halfWidth = _width / 2,
30008 halfHeight = _height / 2;
30009
30010 var direction = ele.pstyle('background-gradient-direction').value;
30011
30012 switch (direction) {
30013 case 'to-bottom':
30014 gradientStyle = context.createLinearGradient(_pos.x, _pos.y - halfHeight, _pos.x, _pos.y + halfHeight);
30015 break;
30016
30017 case 'to-top':
30018 gradientStyle = context.createLinearGradient(_pos.x, _pos.y + halfHeight, _pos.x, _pos.y - halfHeight);
30019 break;
30020
30021 case 'to-left':
30022 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y, _pos.x - halfWidth, _pos.y);
30023 break;
30024
30025 case 'to-right':
30026 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y, _pos.x + halfWidth, _pos.y);
30027 break;
30028
30029 case 'to-bottom-right':
30030 case 'to-right-bottom':
30031 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y - halfHeight, _pos.x + halfWidth, _pos.y + halfHeight);
30032 break;
30033
30034 case 'to-top-right':
30035 case 'to-right-top':
30036 gradientStyle = context.createLinearGradient(_pos.x - halfWidth, _pos.y + halfHeight, _pos.x + halfWidth, _pos.y - halfHeight);
30037 break;
30038
30039 case 'to-bottom-left':
30040 case 'to-left-bottom':
30041 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y - halfHeight, _pos.x - halfWidth, _pos.y + halfHeight);
30042 break;
30043
30044 case 'to-top-left':
30045 case 'to-left-top':
30046 gradientStyle = context.createLinearGradient(_pos.x + halfWidth, _pos.y + halfHeight, _pos.x - halfWidth, _pos.y - halfHeight);
30047 break;
30048 }
30049 }
30050 }
30051
30052 if (!gradientStyle) return null; // invalid gradient style
30053
30054 var hasPositions = positions.length === colors.length;
30055 var length = colors.length;
30056
30057 for (var i = 0; i < length; i++) {
30058 gradientStyle.addColorStop(hasPositions ? positions[i] : i / (length - 1), 'rgba(' + colors[i][0] + ',' + colors[i][1] + ',' + colors[i][2] + ',' + opacity + ')');
30059 }
30060
30061 return gradientStyle;
30062};
30063
30064CRp$6.gradientFillStyle = function (context, ele, fill, opacity) {
30065 var gradientStyle = this.createGradientStyleFor(context, 'background', ele, fill, opacity);
30066 if (!gradientStyle) return null; // error
30067
30068 context.fillStyle = gradientStyle;
30069};
30070
30071CRp$6.colorFillStyle = function (context, r, g, b, a) {
30072 context.fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30073 // var cache = this.paintCache(context);
30074 // var fillStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30075 // if( cache.fillStyle !== fillStyle ){
30076 // context.fillStyle = cache.fillStyle = fillStyle;
30077 // }
30078};
30079
30080CRp$6.eleFillStyle = function (context, ele, opacity) {
30081 var backgroundFill = ele.pstyle('background-fill').value;
30082
30083 if (backgroundFill === 'linear-gradient' || backgroundFill === 'radial-gradient') {
30084 this.gradientFillStyle(context, ele, backgroundFill, opacity);
30085 } else {
30086 var backgroundColor = ele.pstyle('background-color').value;
30087 this.colorFillStyle(context, backgroundColor[0], backgroundColor[1], backgroundColor[2], opacity);
30088 }
30089};
30090
30091CRp$6.gradientStrokeStyle = function (context, ele, fill, opacity) {
30092 var gradientStyle = this.createGradientStyleFor(context, 'line', ele, fill, opacity);
30093 if (!gradientStyle) return null; // error
30094
30095 context.strokeStyle = gradientStyle;
30096};
30097
30098CRp$6.colorStrokeStyle = function (context, r, g, b, a) {
30099 context.strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')'; // turn off for now, seems context does its own caching
30100 // var cache = this.paintCache(context);
30101 // var strokeStyle = 'rgba(' + r + ',' + g + ',' + b + ',' + a + ')';
30102 // if( cache.strokeStyle !== strokeStyle ){
30103 // context.strokeStyle = cache.strokeStyle = strokeStyle;
30104 // }
30105};
30106
30107CRp$6.eleStrokeStyle = function (context, ele, opacity) {
30108 var lineFill = ele.pstyle('line-fill').value;
30109
30110 if (lineFill === 'linear-gradient' || lineFill === 'radial-gradient') {
30111 this.gradientStrokeStyle(context, ele, lineFill, opacity);
30112 } else {
30113 var lineColor = ele.pstyle('line-color').value;
30114 this.colorStrokeStyle(context, lineColor[0], lineColor[1], lineColor[2], opacity);
30115 }
30116}; // Resize canvas
30117
30118
30119CRp$6.matchCanvasSize = function (container) {
30120 var r = this;
30121 var data = r.data;
30122 var bb = r.findContainerClientCoords();
30123 var width = bb[2];
30124 var height = bb[3];
30125 var pixelRatio = r.getPixelRatio();
30126 var mbPxRatio = r.motionBlurPxRatio;
30127
30128 if (container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE] || container === r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG]) {
30129 pixelRatio = mbPxRatio;
30130 }
30131
30132 var canvasWidth = width * pixelRatio;
30133 var canvasHeight = height * pixelRatio;
30134 var canvas;
30135
30136 if (canvasWidth === r.canvasWidth && canvasHeight === r.canvasHeight) {
30137 return; // save cycles if same
30138 }
30139
30140 r.fontCaches = null; // resizing resets the style
30141
30142 var canvasContainer = data.canvasContainer;
30143 canvasContainer.style.width = width + 'px';
30144 canvasContainer.style.height = height + 'px';
30145
30146 for (var i = 0; i < r.CANVAS_LAYERS; i++) {
30147 canvas = data.canvases[i];
30148 canvas.width = canvasWidth;
30149 canvas.height = canvasHeight;
30150 canvas.style.width = width + 'px';
30151 canvas.style.height = height + 'px';
30152 }
30153
30154 for (var i = 0; i < r.BUFFER_COUNT; i++) {
30155 canvas = data.bufferCanvases[i];
30156 canvas.width = canvasWidth;
30157 canvas.height = canvasHeight;
30158 canvas.style.width = width + 'px';
30159 canvas.style.height = height + 'px';
30160 }
30161
30162 r.textureMult = 1;
30163
30164 if (pixelRatio <= 1) {
30165 canvas = data.bufferCanvases[r.TEXTURE_BUFFER];
30166 r.textureMult = 2;
30167 canvas.width = canvasWidth * r.textureMult;
30168 canvas.height = canvasHeight * r.textureMult;
30169 }
30170
30171 r.canvasWidth = canvasWidth;
30172 r.canvasHeight = canvasHeight;
30173};
30174
30175CRp$6.renderTo = function (cxt, zoom, pan, pxRatio) {
30176 this.render({
30177 forcedContext: cxt,
30178 forcedZoom: zoom,
30179 forcedPan: pan,
30180 drawAllLayers: true,
30181 forcedPxRatio: pxRatio
30182 });
30183};
30184
30185CRp$6.render = function (options) {
30186 options = options || staticEmptyObject();
30187 var forcedContext = options.forcedContext;
30188 var drawAllLayers = options.drawAllLayers;
30189 var drawOnlyNodeLayer = options.drawOnlyNodeLayer;
30190 var forcedZoom = options.forcedZoom;
30191 var forcedPan = options.forcedPan;
30192 var r = this;
30193 var pixelRatio = options.forcedPxRatio === undefined ? this.getPixelRatio() : options.forcedPxRatio;
30194 var cy = r.cy;
30195 var data = r.data;
30196 var needDraw = data.canvasNeedsRedraw;
30197 var textureDraw = r.textureOnViewport && !forcedContext && (r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming);
30198 var motionBlur = options.motionBlur !== undefined ? options.motionBlur : r.motionBlur;
30199 var mbPxRatio = r.motionBlurPxRatio;
30200 var hasCompoundNodes = cy.hasCompoundNodes();
30201 var inNodeDragGesture = r.hoverData.draggingEles;
30202 var inBoxSelection = r.hoverData.selecting || r.touchData.selecting ? true : false;
30203 motionBlur = motionBlur && !forcedContext && r.motionBlurEnabled && !inBoxSelection;
30204 var motionBlurFadeEffect = motionBlur;
30205
30206 if (!forcedContext) {
30207 if (r.prevPxRatio !== pixelRatio) {
30208 r.invalidateContainerClientCoordsCache();
30209 r.matchCanvasSize(r.container);
30210 r.redrawHint('eles', true);
30211 r.redrawHint('drag', true);
30212 }
30213
30214 r.prevPxRatio = pixelRatio;
30215 }
30216
30217 if (!forcedContext && r.motionBlurTimeout) {
30218 clearTimeout(r.motionBlurTimeout);
30219 }
30220
30221 if (motionBlur) {
30222 if (r.mbFrames == null) {
30223 r.mbFrames = 0;
30224 }
30225
30226 r.mbFrames++;
30227
30228 if (r.mbFrames < 3) {
30229 // need several frames before even high quality motionblur
30230 motionBlurFadeEffect = false;
30231 } // go to lower quality blurry frames when several m/b frames have been rendered (avoids flashing)
30232
30233
30234 if (r.mbFrames > r.minMbLowQualFrames) {
30235 //r.fullQualityMb = false;
30236 r.motionBlurPxRatio = r.mbPxRBlurry;
30237 }
30238 }
30239
30240 if (r.clearingMotionBlur) {
30241 r.motionBlurPxRatio = 1;
30242 } // b/c drawToContext() may be async w.r.t. redraw(), keep track of last texture frame
30243 // because a rogue async texture frame would clear needDraw
30244
30245
30246 if (r.textureDrawLastFrame && !textureDraw) {
30247 needDraw[r.NODE] = true;
30248 needDraw[r.SELECT_BOX] = true;
30249 }
30250
30251 var style = cy.style();
30252 var zoom = cy.zoom();
30253 var effectiveZoom = forcedZoom !== undefined ? forcedZoom : zoom;
30254 var pan = cy.pan();
30255 var effectivePan = {
30256 x: pan.x,
30257 y: pan.y
30258 };
30259 var vp = {
30260 zoom: zoom,
30261 pan: {
30262 x: pan.x,
30263 y: pan.y
30264 }
30265 };
30266 var prevVp = r.prevViewport;
30267 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)
30268
30269 if (!viewportIsDiff && !(inNodeDragGesture && !hasCompoundNodes)) {
30270 r.motionBlurPxRatio = 1;
30271 }
30272
30273 if (forcedPan) {
30274 effectivePan = forcedPan;
30275 } // apply pixel ratio
30276
30277
30278 effectiveZoom *= pixelRatio;
30279 effectivePan.x *= pixelRatio;
30280 effectivePan.y *= pixelRatio;
30281 var eles = r.getCachedZSortedEles();
30282
30283 function mbclear(context, x, y, w, h) {
30284 var gco = context.globalCompositeOperation;
30285 context.globalCompositeOperation = 'destination-out';
30286 r.colorFillStyle(context, 255, 255, 255, r.motionBlurTransparency);
30287 context.fillRect(x, y, w, h);
30288 context.globalCompositeOperation = gco;
30289 }
30290
30291 function setContextTransform(context, clear) {
30292 var ePan, eZoom, w, h;
30293
30294 if (!r.clearingMotionBlur && (context === data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] || context === data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG])) {
30295 ePan = {
30296 x: pan.x * mbPxRatio,
30297 y: pan.y * mbPxRatio
30298 };
30299 eZoom = zoom * mbPxRatio;
30300 w = r.canvasWidth * mbPxRatio;
30301 h = r.canvasHeight * mbPxRatio;
30302 } else {
30303 ePan = effectivePan;
30304 eZoom = effectiveZoom;
30305 w = r.canvasWidth;
30306 h = r.canvasHeight;
30307 }
30308
30309 context.setTransform(1, 0, 0, 1, 0, 0);
30310
30311 if (clear === 'motionBlur') {
30312 mbclear(context, 0, 0, w, h);
30313 } else if (!forcedContext && (clear === undefined || clear)) {
30314 context.clearRect(0, 0, w, h);
30315 }
30316
30317 if (!drawAllLayers) {
30318 context.translate(ePan.x, ePan.y);
30319 context.scale(eZoom, eZoom);
30320 }
30321
30322 if (forcedPan) {
30323 context.translate(forcedPan.x, forcedPan.y);
30324 }
30325
30326 if (forcedZoom) {
30327 context.scale(forcedZoom, forcedZoom);
30328 }
30329 }
30330
30331 if (!textureDraw) {
30332 r.textureDrawLastFrame = false;
30333 }
30334
30335 if (textureDraw) {
30336 r.textureDrawLastFrame = true;
30337
30338 if (!r.textureCache) {
30339 r.textureCache = {};
30340 r.textureCache.bb = cy.mutableElements().boundingBox();
30341 r.textureCache.texture = r.data.bufferCanvases[r.TEXTURE_BUFFER];
30342 var cxt = r.data.bufferContexts[r.TEXTURE_BUFFER];
30343 cxt.setTransform(1, 0, 0, 1, 0, 0);
30344 cxt.clearRect(0, 0, r.canvasWidth * r.textureMult, r.canvasHeight * r.textureMult);
30345 r.render({
30346 forcedContext: cxt,
30347 drawOnlyNodeLayer: true,
30348 forcedPxRatio: pixelRatio * r.textureMult
30349 });
30350 var vp = r.textureCache.viewport = {
30351 zoom: cy.zoom(),
30352 pan: cy.pan(),
30353 width: r.canvasWidth,
30354 height: r.canvasHeight
30355 };
30356 vp.mpan = {
30357 x: (0 - vp.pan.x) / vp.zoom,
30358 y: (0 - vp.pan.y) / vp.zoom
30359 };
30360 }
30361
30362 needDraw[r.DRAG] = false;
30363 needDraw[r.NODE] = false;
30364 var context = data.contexts[r.NODE];
30365 var texture = r.textureCache.texture;
30366 var vp = r.textureCache.viewport;
30367 context.setTransform(1, 0, 0, 1, 0, 0);
30368
30369 if (motionBlur) {
30370 mbclear(context, 0, 0, vp.width, vp.height);
30371 } else {
30372 context.clearRect(0, 0, vp.width, vp.height);
30373 }
30374
30375 var outsideBgColor = style.core('outside-texture-bg-color').value;
30376 var outsideBgOpacity = style.core('outside-texture-bg-opacity').value;
30377 r.colorFillStyle(context, outsideBgColor[0], outsideBgColor[1], outsideBgColor[2], outsideBgOpacity);
30378 context.fillRect(0, 0, vp.width, vp.height);
30379 var zoom = cy.zoom();
30380 setContextTransform(context, false);
30381 context.clearRect(vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30382 context.drawImage(texture, vp.mpan.x, vp.mpan.y, vp.width / vp.zoom / pixelRatio, vp.height / vp.zoom / pixelRatio);
30383 } else if (r.textureOnViewport && !forcedContext) {
30384 // clear the cache since we don't need it
30385 r.textureCache = null;
30386 }
30387
30388 var extent = cy.extent();
30389 var vpManip = r.pinching || r.hoverData.dragging || r.swipePanning || r.data.wheelZooming || r.hoverData.draggingEles || r.cy.animated();
30390 var hideEdges = r.hideEdgesOnViewport && vpManip;
30391 var needMbClear = [];
30392 needMbClear[r.NODE] = !needDraw[r.NODE] && motionBlur && !r.clearedForMotionBlur[r.NODE] || r.clearingMotionBlur;
30393
30394 if (needMbClear[r.NODE]) {
30395 r.clearedForMotionBlur[r.NODE] = true;
30396 }
30397
30398 needMbClear[r.DRAG] = !needDraw[r.DRAG] && motionBlur && !r.clearedForMotionBlur[r.DRAG] || r.clearingMotionBlur;
30399
30400 if (needMbClear[r.DRAG]) {
30401 r.clearedForMotionBlur[r.DRAG] = true;
30402 }
30403
30404 if (needDraw[r.NODE] || drawAllLayers || drawOnlyNodeLayer || needMbClear[r.NODE]) {
30405 var useBuffer = motionBlur && !needMbClear[r.NODE] && mbPxRatio !== 1;
30406 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_NODE] : data.contexts[r.NODE]);
30407 var clear = motionBlur && !useBuffer ? 'motionBlur' : undefined;
30408 setContextTransform(context, clear);
30409
30410 if (hideEdges) {
30411 r.drawCachedNodes(context, eles.nondrag, pixelRatio, extent);
30412 } else {
30413 r.drawLayeredElements(context, eles.nondrag, pixelRatio, extent);
30414 }
30415
30416 if (r.debug) {
30417 r.drawDebugPoints(context, eles.nondrag);
30418 }
30419
30420 if (!drawAllLayers && !motionBlur) {
30421 needDraw[r.NODE] = false;
30422 }
30423 }
30424
30425 if (!drawOnlyNodeLayer && (needDraw[r.DRAG] || drawAllLayers || needMbClear[r.DRAG])) {
30426 var useBuffer = motionBlur && !needMbClear[r.DRAG] && mbPxRatio !== 1;
30427 var context = forcedContext || (useBuffer ? r.data.bufferContexts[r.MOTIONBLUR_BUFFER_DRAG] : data.contexts[r.DRAG]);
30428 setContextTransform(context, motionBlur && !useBuffer ? 'motionBlur' : undefined);
30429
30430 if (hideEdges) {
30431 r.drawCachedNodes(context, eles.drag, pixelRatio, extent);
30432 } else {
30433 r.drawCachedElements(context, eles.drag, pixelRatio, extent);
30434 }
30435
30436 if (r.debug) {
30437 r.drawDebugPoints(context, eles.drag);
30438 }
30439
30440 if (!drawAllLayers && !motionBlur) {
30441 needDraw[r.DRAG] = false;
30442 }
30443 }
30444
30445 if (r.showFps || !drawOnlyNodeLayer && needDraw[r.SELECT_BOX] && !drawAllLayers) {
30446 var context = forcedContext || data.contexts[r.SELECT_BOX];
30447 setContextTransform(context);
30448
30449 if (r.selection[4] == 1 && (r.hoverData.selecting || r.touchData.selecting)) {
30450 var zoom = r.cy.zoom();
30451 var borderWidth = style.core('selection-box-border-width').value / zoom;
30452 context.lineWidth = borderWidth;
30453 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 + ')';
30454 context.fillRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30455
30456 if (borderWidth > 0) {
30457 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 + ')';
30458 context.strokeRect(r.selection[0], r.selection[1], r.selection[2] - r.selection[0], r.selection[3] - r.selection[1]);
30459 }
30460 }
30461
30462 if (data.bgActivePosistion && !r.hoverData.selecting) {
30463 var zoom = r.cy.zoom();
30464 var pos = data.bgActivePosistion;
30465 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 + ')';
30466 context.beginPath();
30467 context.arc(pos.x, pos.y, style.core('active-bg-size').pfValue / zoom, 0, 2 * Math.PI);
30468 context.fill();
30469 }
30470
30471 var timeToRender = r.lastRedrawTime;
30472
30473 if (r.showFps && timeToRender) {
30474 timeToRender = Math.round(timeToRender);
30475 var fps = Math.round(1000 / timeToRender);
30476 context.setTransform(1, 0, 0, 1, 0, 0);
30477 context.fillStyle = 'rgba(255, 0, 0, 0.75)';
30478 context.strokeStyle = 'rgba(255, 0, 0, 0.75)';
30479 context.lineWidth = 1;
30480 context.fillText('1 frame = ' + timeToRender + ' ms = ' + fps + ' fps', 0, 20);
30481 var maxFps = 60;
30482 context.strokeRect(0, 30, 250, 20);
30483 context.fillRect(0, 30, 250 * Math.min(fps / maxFps, 1), 20);
30484 }
30485
30486 if (!drawAllLayers) {
30487 needDraw[r.SELECT_BOX] = false;
30488 }
30489 } // motionblur: blit rendered blurry frames
30490
30491
30492 if (motionBlur && mbPxRatio !== 1) {
30493 var cxtNode = data.contexts[r.NODE];
30494 var txtNode = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_NODE];
30495 var cxtDrag = data.contexts[r.DRAG];
30496 var txtDrag = r.data.bufferCanvases[r.MOTIONBLUR_BUFFER_DRAG];
30497
30498 var drawMotionBlur = function drawMotionBlur(cxt, txt, needClear) {
30499 cxt.setTransform(1, 0, 0, 1, 0, 0);
30500
30501 if (needClear || !motionBlurFadeEffect) {
30502 cxt.clearRect(0, 0, r.canvasWidth, r.canvasHeight);
30503 } else {
30504 mbclear(cxt, 0, 0, r.canvasWidth, r.canvasHeight);
30505 }
30506
30507 var pxr = mbPxRatio;
30508 cxt.drawImage(txt, // img
30509 0, 0, // sx, sy
30510 r.canvasWidth * pxr, r.canvasHeight * pxr, // sw, sh
30511 0, 0, // x, y
30512 r.canvasWidth, r.canvasHeight // w, h
30513 );
30514 };
30515
30516 if (needDraw[r.NODE] || needMbClear[r.NODE]) {
30517 drawMotionBlur(cxtNode, txtNode, needMbClear[r.NODE]);
30518 needDraw[r.NODE] = false;
30519 }
30520
30521 if (needDraw[r.DRAG] || needMbClear[r.DRAG]) {
30522 drawMotionBlur(cxtDrag, txtDrag, needMbClear[r.DRAG]);
30523 needDraw[r.DRAG] = false;
30524 }
30525 }
30526
30527 r.prevViewport = vp;
30528
30529 if (r.clearingMotionBlur) {
30530 r.clearingMotionBlur = false;
30531 r.motionBlurCleared = true;
30532 r.motionBlur = true;
30533 }
30534
30535 if (motionBlur) {
30536 r.motionBlurTimeout = setTimeout(function () {
30537 r.motionBlurTimeout = null;
30538 r.clearedForMotionBlur[r.NODE] = false;
30539 r.clearedForMotionBlur[r.DRAG] = false;
30540 r.motionBlur = false;
30541 r.clearingMotionBlur = !textureDraw;
30542 r.mbFrames = 0;
30543 needDraw[r.NODE] = true;
30544 needDraw[r.DRAG] = true;
30545 r.redraw();
30546 }, motionBlurDelay);
30547 }
30548
30549 if (!forcedContext) {
30550 cy.emit('render');
30551 }
30552};
30553
30554var CRp$7 = {}; // @O Polygon drawing
30555
30556CRp$7.drawPolygonPath = function (context, x, y, width, height, points) {
30557 var halfW = width / 2;
30558 var halfH = height / 2;
30559
30560 if (context.beginPath) {
30561 context.beginPath();
30562 }
30563
30564 context.moveTo(x + halfW * points[0], y + halfH * points[1]);
30565
30566 for (var i = 1; i < points.length / 2; i++) {
30567 context.lineTo(x + halfW * points[i * 2], y + halfH * points[i * 2 + 1]);
30568 }
30569
30570 context.closePath();
30571};
30572
30573CRp$7.drawRoundPolygonPath = function (context, x, y, width, height, points) {
30574 var halfW = width / 2;
30575 var halfH = height / 2;
30576 var cornerRadius = getRoundPolygonRadius(width, height);
30577
30578 if (context.beginPath) {
30579 context.beginPath();
30580 }
30581
30582 for (var _i = 0; _i < points.length / 4; _i++) {
30583 var sourceUv = void 0,
30584 destUv = void 0;
30585
30586 if (_i === 0) {
30587 sourceUv = points.length - 2;
30588 } else {
30589 sourceUv = _i * 4 - 2;
30590 }
30591
30592 destUv = _i * 4 + 2;
30593 var px = x + halfW * points[_i * 4];
30594 var py = y + halfH * points[_i * 4 + 1];
30595 var cosTheta = -points[sourceUv] * points[destUv] - points[sourceUv + 1] * points[destUv + 1];
30596 var offset = cornerRadius / Math.tan(Math.acos(cosTheta) / 2);
30597 var cp0x = px - offset * points[sourceUv];
30598 var cp0y = py - offset * points[sourceUv + 1];
30599 var cp1x = px + offset * points[destUv];
30600 var cp1y = py + offset * points[destUv + 1];
30601
30602 if (_i === 0) {
30603 context.moveTo(cp0x, cp0y);
30604 } else {
30605 context.lineTo(cp0x, cp0y);
30606 }
30607
30608 context.arcTo(px, py, cp1x, cp1y, cornerRadius);
30609 }
30610
30611 context.closePath();
30612}; // Round rectangle drawing
30613
30614
30615CRp$7.drawRoundRectanglePath = function (context, x, y, width, height) {
30616 var halfWidth = width / 2;
30617 var halfHeight = height / 2;
30618 var cornerRadius = getRoundRectangleRadius(width, height);
30619
30620 if (context.beginPath) {
30621 context.beginPath();
30622 } // Start at top middle
30623
30624
30625 context.moveTo(x, y - halfHeight); // Arc from middle top to right side
30626
30627 context.arcTo(x + halfWidth, y - halfHeight, x + halfWidth, y, cornerRadius); // Arc from right side to bottom
30628
30629 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius); // Arc from bottom to left side
30630
30631 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius); // Arc from left side to topBorder
30632
30633 context.arcTo(x - halfWidth, y - halfHeight, x, y - halfHeight, cornerRadius); // Join line
30634
30635 context.lineTo(x, y - halfHeight);
30636 context.closePath();
30637};
30638
30639CRp$7.drawBottomRoundRectanglePath = function (context, x, y, width, height) {
30640 var halfWidth = width / 2;
30641 var halfHeight = height / 2;
30642 var cornerRadius = getRoundRectangleRadius(width, height);
30643
30644 if (context.beginPath) {
30645 context.beginPath();
30646 } // Start at top middle
30647
30648
30649 context.moveTo(x, y - halfHeight);
30650 context.lineTo(x + halfWidth, y - halfHeight);
30651 context.lineTo(x + halfWidth, y);
30652 context.arcTo(x + halfWidth, y + halfHeight, x, y + halfHeight, cornerRadius);
30653 context.arcTo(x - halfWidth, y + halfHeight, x - halfWidth, y, cornerRadius);
30654 context.lineTo(x - halfWidth, y - halfHeight);
30655 context.lineTo(x, y - halfHeight);
30656 context.closePath();
30657};
30658
30659CRp$7.drawCutRectanglePath = function (context, x, y, width, height) {
30660 var halfWidth = width / 2;
30661 var halfHeight = height / 2;
30662 var cornerLength = getCutRectangleCornerLength();
30663
30664 if (context.beginPath) {
30665 context.beginPath();
30666 }
30667
30668 context.moveTo(x - halfWidth + cornerLength, y - halfHeight);
30669 context.lineTo(x + halfWidth - cornerLength, y - halfHeight);
30670 context.lineTo(x + halfWidth, y - halfHeight + cornerLength);
30671 context.lineTo(x + halfWidth, y + halfHeight - cornerLength);
30672 context.lineTo(x + halfWidth - cornerLength, y + halfHeight);
30673 context.lineTo(x - halfWidth + cornerLength, y + halfHeight);
30674 context.lineTo(x - halfWidth, y + halfHeight - cornerLength);
30675 context.lineTo(x - halfWidth, y - halfHeight + cornerLength);
30676 context.closePath();
30677};
30678
30679CRp$7.drawBarrelPath = function (context, x, y, width, height) {
30680 var halfWidth = width / 2;
30681 var halfHeight = height / 2;
30682 var xBegin = x - halfWidth;
30683 var xEnd = x + halfWidth;
30684 var yBegin = y - halfHeight;
30685 var yEnd = y + halfHeight;
30686 var barrelCurveConstants = getBarrelCurveConstants(width, height);
30687 var wOffset = barrelCurveConstants.widthOffset;
30688 var hOffset = barrelCurveConstants.heightOffset;
30689 var ctrlPtXOffset = barrelCurveConstants.ctrlPtOffsetPct * wOffset;
30690
30691 if (context.beginPath) {
30692 context.beginPath();
30693 }
30694
30695 context.moveTo(xBegin, yBegin + hOffset);
30696 context.lineTo(xBegin, yEnd - hOffset);
30697 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yEnd, xBegin + wOffset, yEnd);
30698 context.lineTo(xEnd - wOffset, yEnd);
30699 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yEnd, xEnd, yEnd - hOffset);
30700 context.lineTo(xEnd, yBegin + hOffset);
30701 context.quadraticCurveTo(xEnd - ctrlPtXOffset, yBegin, xEnd - wOffset, yBegin);
30702 context.lineTo(xBegin + wOffset, yBegin);
30703 context.quadraticCurveTo(xBegin + ctrlPtXOffset, yBegin, xBegin, yBegin + hOffset);
30704 context.closePath();
30705};
30706
30707var sin0 = Math.sin(0);
30708var cos0 = Math.cos(0);
30709var sin = {};
30710var cos = {};
30711var ellipseStepSize = Math.PI / 40;
30712
30713for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30714 sin[i] = Math.sin(i);
30715 cos[i] = Math.cos(i);
30716}
30717
30718CRp$7.drawEllipsePath = function (context, centerX, centerY, width, height) {
30719 if (context.beginPath) {
30720 context.beginPath();
30721 }
30722
30723 if (context.ellipse) {
30724 context.ellipse(centerX, centerY, width / 2, height / 2, 0, 0, 2 * Math.PI);
30725 } else {
30726 var xPos, yPos;
30727 var rw = width / 2;
30728 var rh = height / 2;
30729
30730 for (var i = 0 * Math.PI; i < 2 * Math.PI; i += ellipseStepSize) {
30731 xPos = centerX - rw * sin[i] * sin0 + rw * cos[i] * cos0;
30732 yPos = centerY + rh * cos[i] * sin0 + rh * sin[i] * cos0;
30733
30734 if (i === 0) {
30735 context.moveTo(xPos, yPos);
30736 } else {
30737 context.lineTo(xPos, yPos);
30738 }
30739 }
30740 }
30741
30742 context.closePath();
30743};
30744
30745/* global atob, ArrayBuffer, Uint8Array, Blob */
30746var CRp$8 = {};
30747
30748CRp$8.createBuffer = function (w, h) {
30749 var buffer = document.createElement('canvas'); // eslint-disable-line no-undef
30750
30751 buffer.width = w;
30752 buffer.height = h;
30753 return [buffer, buffer.getContext('2d')];
30754};
30755
30756CRp$8.bufferCanvasImage = function (options) {
30757 var cy = this.cy;
30758 var eles = cy.mutableElements();
30759 var bb = eles.boundingBox();
30760 var ctrRect = this.findContainerClientCoords();
30761 var width = options.full ? Math.ceil(bb.w) : ctrRect[2];
30762 var height = options.full ? Math.ceil(bb.h) : ctrRect[3];
30763 var specdMaxDims = number(options.maxWidth) || number(options.maxHeight);
30764 var pxRatio = this.getPixelRatio();
30765 var scale = 1;
30766
30767 if (options.scale !== undefined) {
30768 width *= options.scale;
30769 height *= options.scale;
30770 scale = options.scale;
30771 } else if (specdMaxDims) {
30772 var maxScaleW = Infinity;
30773 var maxScaleH = Infinity;
30774
30775 if (number(options.maxWidth)) {
30776 maxScaleW = scale * options.maxWidth / width;
30777 }
30778
30779 if (number(options.maxHeight)) {
30780 maxScaleH = scale * options.maxHeight / height;
30781 }
30782
30783 scale = Math.min(maxScaleW, maxScaleH);
30784 width *= scale;
30785 height *= scale;
30786 }
30787
30788 if (!specdMaxDims) {
30789 width *= pxRatio;
30790 height *= pxRatio;
30791 scale *= pxRatio;
30792 }
30793
30794 var buffCanvas = document.createElement('canvas'); // eslint-disable-line no-undef
30795
30796 buffCanvas.width = width;
30797 buffCanvas.height = height;
30798 buffCanvas.style.width = width + 'px';
30799 buffCanvas.style.height = height + 'px';
30800 var buffCxt = buffCanvas.getContext('2d'); // Rasterize the layers, but only if container has nonzero size
30801
30802 if (width > 0 && height > 0) {
30803 buffCxt.clearRect(0, 0, width, height);
30804 buffCxt.globalCompositeOperation = 'source-over';
30805 var zsortedEles = this.getCachedZSortedEles();
30806
30807 if (options.full) {
30808 // draw the full bounds of the graph
30809 buffCxt.translate(-bb.x1 * scale, -bb.y1 * scale);
30810 buffCxt.scale(scale, scale);
30811 this.drawElements(buffCxt, zsortedEles);
30812 buffCxt.scale(1 / scale, 1 / scale);
30813 buffCxt.translate(bb.x1 * scale, bb.y1 * scale);
30814 } else {
30815 // draw the current view
30816 var pan = cy.pan();
30817 var translation = {
30818 x: pan.x * scale,
30819 y: pan.y * scale
30820 };
30821 scale *= cy.zoom();
30822 buffCxt.translate(translation.x, translation.y);
30823 buffCxt.scale(scale, scale);
30824 this.drawElements(buffCxt, zsortedEles);
30825 buffCxt.scale(1 / scale, 1 / scale);
30826 buffCxt.translate(-translation.x, -translation.y);
30827 } // need to fill bg at end like this in order to fill cleared transparent pixels in jpgs
30828
30829
30830 if (options.bg) {
30831 buffCxt.globalCompositeOperation = 'destination-over';
30832 buffCxt.fillStyle = options.bg;
30833 buffCxt.rect(0, 0, width, height);
30834 buffCxt.fill();
30835 }
30836 }
30837
30838 return buffCanvas;
30839};
30840
30841function b64ToBlob(b64, mimeType) {
30842 var bytes = atob(b64);
30843 var buff = new ArrayBuffer(bytes.length);
30844 var buffUint8 = new Uint8Array(buff);
30845
30846 for (var i = 0; i < bytes.length; i++) {
30847 buffUint8[i] = bytes.charCodeAt(i);
30848 }
30849
30850 return new Blob([buff], {
30851 type: mimeType
30852 });
30853}
30854
30855function b64UriToB64(b64uri) {
30856 var i = b64uri.indexOf(',');
30857 return b64uri.substr(i + 1);
30858}
30859
30860function output(options, canvas, mimeType) {
30861 var getB64Uri = function getB64Uri() {
30862 return canvas.toDataURL(mimeType, options.quality);
30863 };
30864
30865 switch (options.output) {
30866 case 'blob-promise':
30867 return new Promise$1(function (resolve, reject) {
30868 try {
30869 canvas.toBlob(function (blob) {
30870 if (blob != null) {
30871 resolve(blob);
30872 } else {
30873 reject(new Error('`canvas.toBlob()` sent a null value in its callback'));
30874 }
30875 }, mimeType, options.quality);
30876 } catch (err) {
30877 reject(err);
30878 }
30879 });
30880
30881 case 'blob':
30882 return b64ToBlob(b64UriToB64(getB64Uri()), mimeType);
30883
30884 case 'base64':
30885 return b64UriToB64(getB64Uri());
30886
30887 case 'base64uri':
30888 default:
30889 return getB64Uri();
30890 }
30891}
30892
30893CRp$8.png = function (options) {
30894 return output(options, this.bufferCanvasImage(options), 'image/png');
30895};
30896
30897CRp$8.jpg = function (options) {
30898 return output(options, this.bufferCanvasImage(options), 'image/jpeg');
30899};
30900
30901var CRp$9 = {};
30902
30903CRp$9.nodeShapeImpl = function (name, context, centerX, centerY, width, height, points) {
30904 switch (name) {
30905 case 'ellipse':
30906 return this.drawEllipsePath(context, centerX, centerY, width, height);
30907
30908 case 'polygon':
30909 return this.drawPolygonPath(context, centerX, centerY, width, height, points);
30910
30911 case 'round-polygon':
30912 return this.drawRoundPolygonPath(context, centerX, centerY, width, height, points);
30913
30914 case 'roundrectangle':
30915 case 'round-rectangle':
30916 return this.drawRoundRectanglePath(context, centerX, centerY, width, height);
30917
30918 case 'cutrectangle':
30919 case 'cut-rectangle':
30920 return this.drawCutRectanglePath(context, centerX, centerY, width, height);
30921
30922 case 'bottomroundrectangle':
30923 case 'bottom-round-rectangle':
30924 return this.drawBottomRoundRectanglePath(context, centerX, centerY, width, height);
30925
30926 case 'barrel':
30927 return this.drawBarrelPath(context, centerX, centerY, width, height);
30928 }
30929};
30930
30931var CR = CanvasRenderer;
30932var CRp$a = CanvasRenderer.prototype;
30933CRp$a.CANVAS_LAYERS = 3; //
30934
30935CRp$a.SELECT_BOX = 0;
30936CRp$a.DRAG = 1;
30937CRp$a.NODE = 2;
30938CRp$a.BUFFER_COUNT = 3; //
30939
30940CRp$a.TEXTURE_BUFFER = 0;
30941CRp$a.MOTIONBLUR_BUFFER_NODE = 1;
30942CRp$a.MOTIONBLUR_BUFFER_DRAG = 2;
30943
30944function CanvasRenderer(options) {
30945 var r = this;
30946 r.data = {
30947 canvases: new Array(CRp$a.CANVAS_LAYERS),
30948 contexts: new Array(CRp$a.CANVAS_LAYERS),
30949 canvasNeedsRedraw: new Array(CRp$a.CANVAS_LAYERS),
30950 bufferCanvases: new Array(CRp$a.BUFFER_COUNT),
30951 bufferContexts: new Array(CRp$a.CANVAS_LAYERS)
30952 };
30953 var tapHlOffAttr = '-webkit-tap-highlight-color';
30954 var tapHlOffStyle = 'rgba(0,0,0,0)';
30955 r.data.canvasContainer = document.createElement('div'); // eslint-disable-line no-undef
30956
30957 var containerStyle = r.data.canvasContainer.style;
30958 r.data.canvasContainer.style[tapHlOffAttr] = tapHlOffStyle;
30959 containerStyle.position = 'relative';
30960 containerStyle.zIndex = '0';
30961 containerStyle.overflow = 'hidden';
30962 var container = options.cy.container();
30963 container.appendChild(r.data.canvasContainer);
30964 container.style[tapHlOffAttr] = tapHlOffStyle;
30965 var styleMap = {
30966 '-webkit-user-select': 'none',
30967 '-moz-user-select': '-moz-none',
30968 'user-select': 'none',
30969 '-webkit-tap-highlight-color': 'rgba(0,0,0,0)',
30970 'outline-style': 'none'
30971 };
30972
30973 if (ms()) {
30974 styleMap['-ms-touch-action'] = 'none';
30975 styleMap['touch-action'] = 'none';
30976 }
30977
30978 for (var i = 0; i < CRp$a.CANVAS_LAYERS; i++) {
30979 var canvas = r.data.canvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30980
30981 r.data.contexts[i] = canvas.getContext('2d');
30982 Object.keys(styleMap).forEach(function (k) {
30983 canvas.style[k] = styleMap[k];
30984 });
30985 canvas.style.position = 'absolute';
30986 canvas.setAttribute('data-id', 'layer' + i);
30987 canvas.style.zIndex = String(CRp$a.CANVAS_LAYERS - i);
30988 r.data.canvasContainer.appendChild(canvas);
30989 r.data.canvasNeedsRedraw[i] = false;
30990 }
30991
30992 r.data.topCanvas = r.data.canvases[0];
30993 r.data.canvases[CRp$a.NODE].setAttribute('data-id', 'layer' + CRp$a.NODE + '-node');
30994 r.data.canvases[CRp$a.SELECT_BOX].setAttribute('data-id', 'layer' + CRp$a.SELECT_BOX + '-selectbox');
30995 r.data.canvases[CRp$a.DRAG].setAttribute('data-id', 'layer' + CRp$a.DRAG + '-drag');
30996
30997 for (var i = 0; i < CRp$a.BUFFER_COUNT; i++) {
30998 r.data.bufferCanvases[i] = document.createElement('canvas'); // eslint-disable-line no-undef
30999
31000 r.data.bufferContexts[i] = r.data.bufferCanvases[i].getContext('2d');
31001 r.data.bufferCanvases[i].style.position = 'absolute';
31002 r.data.bufferCanvases[i].setAttribute('data-id', 'buffer' + i);
31003 r.data.bufferCanvases[i].style.zIndex = String(-i - 1);
31004 r.data.bufferCanvases[i].style.visibility = 'hidden'; //r.data.canvasContainer.appendChild(r.data.bufferCanvases[i]);
31005 }
31006
31007 r.pathsEnabled = true;
31008 var emptyBb = makeBoundingBox();
31009
31010 var getBoxCenter = function getBoxCenter(bb) {
31011 return {
31012 x: (bb.x1 + bb.x2) / 2,
31013 y: (bb.y1 + bb.y2) / 2
31014 };
31015 };
31016
31017 var getCenterOffset = function getCenterOffset(bb) {
31018 return {
31019 x: -bb.w / 2,
31020 y: -bb.h / 2
31021 };
31022 };
31023
31024 var backgroundTimestampHasChanged = function backgroundTimestampHasChanged(ele) {
31025 var _p = ele[0]._private;
31026 var same = _p.oldBackgroundTimestamp === _p.backgroundTimestamp;
31027 return !same;
31028 };
31029
31030 var getStyleKey = function getStyleKey(ele) {
31031 return ele[0]._private.nodeKey;
31032 };
31033
31034 var getLabelKey = function getLabelKey(ele) {
31035 return ele[0]._private.labelStyleKey;
31036 };
31037
31038 var getSourceLabelKey = function getSourceLabelKey(ele) {
31039 return ele[0]._private.sourceLabelStyleKey;
31040 };
31041
31042 var getTargetLabelKey = function getTargetLabelKey(ele) {
31043 return ele[0]._private.targetLabelStyleKey;
31044 };
31045
31046 var drawElement = function drawElement(context, ele, bb, scaledLabelShown, useEleOpacity) {
31047 return r.drawElement(context, ele, bb, false, false, useEleOpacity);
31048 };
31049
31050 var drawLabel = function drawLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31051 return r.drawElementText(context, ele, bb, scaledLabelShown, 'main', useEleOpacity);
31052 };
31053
31054 var drawSourceLabel = function drawSourceLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31055 return r.drawElementText(context, ele, bb, scaledLabelShown, 'source', useEleOpacity);
31056 };
31057
31058 var drawTargetLabel = function drawTargetLabel(context, ele, bb, scaledLabelShown, useEleOpacity) {
31059 return r.drawElementText(context, ele, bb, scaledLabelShown, 'target', useEleOpacity);
31060 };
31061
31062 var getElementBox = function getElementBox(ele) {
31063 ele.boundingBox();
31064 return ele[0]._private.bodyBounds;
31065 };
31066
31067 var getLabelBox = function getLabelBox(ele) {
31068 ele.boundingBox();
31069 return ele[0]._private.labelBounds.main || emptyBb;
31070 };
31071
31072 var getSourceLabelBox = function getSourceLabelBox(ele) {
31073 ele.boundingBox();
31074 return ele[0]._private.labelBounds.source || emptyBb;
31075 };
31076
31077 var getTargetLabelBox = function getTargetLabelBox(ele) {
31078 ele.boundingBox();
31079 return ele[0]._private.labelBounds.target || emptyBb;
31080 };
31081
31082 var isLabelVisibleAtScale = function isLabelVisibleAtScale(ele, scaledLabelShown) {
31083 return scaledLabelShown;
31084 };
31085
31086 var getElementRotationPoint = function getElementRotationPoint(ele) {
31087 return getBoxCenter(getElementBox(ele));
31088 };
31089
31090 var addTextMargin = function addTextMargin(prefix, pt, ele) {
31091 var pre = prefix ? prefix + '-' : '';
31092 return {
31093 x: pt.x + ele.pstyle(pre + 'text-margin-x').pfValue,
31094 y: pt.y + ele.pstyle(pre + 'text-margin-y').pfValue
31095 };
31096 };
31097
31098 var getRsPt = function getRsPt(ele, x, y) {
31099 var rs = ele[0]._private.rscratch;
31100 return {
31101 x: rs[x],
31102 y: rs[y]
31103 };
31104 };
31105
31106 var getLabelRotationPoint = function getLabelRotationPoint(ele) {
31107 return addTextMargin('', getRsPt(ele, 'labelX', 'labelY'), ele);
31108 };
31109
31110 var getSourceLabelRotationPoint = function getSourceLabelRotationPoint(ele) {
31111 return addTextMargin('source', getRsPt(ele, 'sourceLabelX', 'sourceLabelY'), ele);
31112 };
31113
31114 var getTargetLabelRotationPoint = function getTargetLabelRotationPoint(ele) {
31115 return addTextMargin('target', getRsPt(ele, 'targetLabelX', 'targetLabelY'), ele);
31116 };
31117
31118 var getElementRotationOffset = function getElementRotationOffset(ele) {
31119 return getCenterOffset(getElementBox(ele));
31120 };
31121
31122 var getSourceLabelRotationOffset = function getSourceLabelRotationOffset(ele) {
31123 return getCenterOffset(getSourceLabelBox(ele));
31124 };
31125
31126 var getTargetLabelRotationOffset = function getTargetLabelRotationOffset(ele) {
31127 return getCenterOffset(getTargetLabelBox(ele));
31128 };
31129
31130 var getLabelRotationOffset = function getLabelRotationOffset(ele) {
31131 var bb = getLabelBox(ele);
31132 var p = getCenterOffset(getLabelBox(ele));
31133
31134 if (ele.isNode()) {
31135 switch (ele.pstyle('text-halign').value) {
31136 case 'left':
31137 p.x = -bb.w;
31138 break;
31139
31140 case 'right':
31141 p.x = 0;
31142 break;
31143 }
31144
31145 switch (ele.pstyle('text-valign').value) {
31146 case 'top':
31147 p.y = -bb.h;
31148 break;
31149
31150 case 'bottom':
31151 p.y = 0;
31152 break;
31153 }
31154 }
31155
31156 return p;
31157 };
31158
31159 var eleTxrCache = r.data.eleTxrCache = new ElementTextureCache(r, {
31160 getKey: getStyleKey,
31161 doesEleInvalidateKey: backgroundTimestampHasChanged,
31162 drawElement: drawElement,
31163 getBoundingBox: getElementBox,
31164 getRotationPoint: getElementRotationPoint,
31165 getRotationOffset: getElementRotationOffset,
31166 allowEdgeTxrCaching: false,
31167 allowParentTxrCaching: false
31168 });
31169 var lblTxrCache = r.data.lblTxrCache = new ElementTextureCache(r, {
31170 getKey: getLabelKey,
31171 drawElement: drawLabel,
31172 getBoundingBox: getLabelBox,
31173 getRotationPoint: getLabelRotationPoint,
31174 getRotationOffset: getLabelRotationOffset,
31175 isVisible: isLabelVisibleAtScale
31176 });
31177 var slbTxrCache = r.data.slbTxrCache = new ElementTextureCache(r, {
31178 getKey: getSourceLabelKey,
31179 drawElement: drawSourceLabel,
31180 getBoundingBox: getSourceLabelBox,
31181 getRotationPoint: getSourceLabelRotationPoint,
31182 getRotationOffset: getSourceLabelRotationOffset,
31183 isVisible: isLabelVisibleAtScale
31184 });
31185 var tlbTxrCache = r.data.tlbTxrCache = new ElementTextureCache(r, {
31186 getKey: getTargetLabelKey,
31187 drawElement: drawTargetLabel,
31188 getBoundingBox: getTargetLabelBox,
31189 getRotationPoint: getTargetLabelRotationPoint,
31190 getRotationOffset: getTargetLabelRotationOffset,
31191 isVisible: isLabelVisibleAtScale
31192 });
31193 var lyrTxrCache = r.data.lyrTxrCache = new LayeredTextureCache(r);
31194 r.onUpdateEleCalcs(function invalidateTextureCaches(willDraw, eles) {
31195 // each cache should check for sub-key diff to see that the update affects that cache particularly
31196 eleTxrCache.invalidateElements(eles);
31197 lblTxrCache.invalidateElements(eles);
31198 slbTxrCache.invalidateElements(eles);
31199 tlbTxrCache.invalidateElements(eles); // any change invalidates the layers
31200
31201 lyrTxrCache.invalidateElements(eles); // update the old bg timestamp so diffs can be done in the ele txr caches
31202
31203 for (var _i = 0; _i < eles.length; _i++) {
31204 var _p = eles[_i]._private;
31205 _p.oldBackgroundTimestamp = _p.backgroundTimestamp;
31206 }
31207 });
31208
31209 var refineInLayers = function refineInLayers(reqs) {
31210 for (var i = 0; i < reqs.length; i++) {
31211 lyrTxrCache.enqueueElementRefinement(reqs[i].ele);
31212 }
31213 };
31214
31215 eleTxrCache.onDequeue(refineInLayers);
31216 lblTxrCache.onDequeue(refineInLayers);
31217 slbTxrCache.onDequeue(refineInLayers);
31218 tlbTxrCache.onDequeue(refineInLayers);
31219}
31220
31221CRp$a.redrawHint = function (group, bool) {
31222 var r = this;
31223
31224 switch (group) {
31225 case 'eles':
31226 r.data.canvasNeedsRedraw[CRp$a.NODE] = bool;
31227 break;
31228
31229 case 'drag':
31230 r.data.canvasNeedsRedraw[CRp$a.DRAG] = bool;
31231 break;
31232
31233 case 'select':
31234 r.data.canvasNeedsRedraw[CRp$a.SELECT_BOX] = bool;
31235 break;
31236 }
31237}; // whether to use Path2D caching for drawing
31238
31239
31240var pathsImpld = typeof Path2D !== 'undefined';
31241
31242CRp$a.path2dEnabled = function (on) {
31243 if (on === undefined) {
31244 return this.pathsEnabled;
31245 }
31246
31247 this.pathsEnabled = on ? true : false;
31248};
31249
31250CRp$a.usePaths = function () {
31251 return pathsImpld && this.pathsEnabled;
31252};
31253
31254CRp$a.setImgSmoothing = function (context, bool) {
31255 if (context.imageSmoothingEnabled != null) {
31256 context.imageSmoothingEnabled = bool;
31257 } else {
31258 context.webkitImageSmoothingEnabled = bool;
31259 context.mozImageSmoothingEnabled = bool;
31260 context.msImageSmoothingEnabled = bool;
31261 }
31262};
31263
31264CRp$a.getImgSmoothing = function (context) {
31265 if (context.imageSmoothingEnabled != null) {
31266 return context.imageSmoothingEnabled;
31267 } else {
31268 return context.webkitImageSmoothingEnabled || context.mozImageSmoothingEnabled || context.msImageSmoothingEnabled;
31269 }
31270};
31271
31272CRp$a.makeOffscreenCanvas = function (width, height) {
31273 var canvas;
31274
31275 if ((typeof OffscreenCanvas === "undefined" ? "undefined" : _typeof(OffscreenCanvas)) !== ( "undefined" )) {
31276 canvas = new OffscreenCanvas(width, height);
31277 } else {
31278 canvas = document.createElement('canvas'); // eslint-disable-line no-undef
31279
31280 canvas.width = width;
31281 canvas.height = height;
31282 }
31283
31284 return canvas;
31285};
31286
31287[CRp, CRp$1, CRp$2, CRp$3, CRp$4, CRp$5, CRp$6, CRp$7, CRp$8, CRp$9].forEach(function (props) {
31288 extend(CRp$a, props);
31289});
31290
31291var renderer = [{
31292 name: 'null',
31293 impl: NullRenderer
31294}, {
31295 name: 'base',
31296 impl: BR
31297}, {
31298 name: 'canvas',
31299 impl: CR
31300}];
31301
31302var incExts = [{
31303 type: 'layout',
31304 extensions: layout
31305}, {
31306 type: 'renderer',
31307 extensions: renderer
31308}];
31309
31310var extensions = {}; // registered modules for extensions, indexed by name
31311
31312var modules = {};
31313
31314function setExtension(type, name, registrant) {
31315 var ext = registrant;
31316
31317 var overrideErr = function overrideErr(field) {
31318 warn('Can not register `' + name + '` for `' + type + '` since `' + field + '` already exists in the prototype and can not be overridden');
31319 };
31320
31321 if (type === 'core') {
31322 if (Core.prototype[name]) {
31323 return overrideErr(name);
31324 } else {
31325 Core.prototype[name] = registrant;
31326 }
31327 } else if (type === 'collection') {
31328 if (Collection.prototype[name]) {
31329 return overrideErr(name);
31330 } else {
31331 Collection.prototype[name] = registrant;
31332 }
31333 } else if (type === 'layout') {
31334 // fill in missing layout functions in the prototype
31335 var Layout = function Layout(options) {
31336 this.options = options;
31337 registrant.call(this, options); // make sure layout has _private for use w/ std apis like .on()
31338
31339 if (!plainObject(this._private)) {
31340 this._private = {};
31341 }
31342
31343 this._private.cy = options.cy;
31344 this._private.listeners = [];
31345 this.createEmitter();
31346 };
31347
31348 var layoutProto = Layout.prototype = Object.create(registrant.prototype);
31349 var optLayoutFns = [];
31350
31351 for (var i = 0; i < optLayoutFns.length; i++) {
31352 var fnName = optLayoutFns[i];
31353
31354 layoutProto[fnName] = layoutProto[fnName] || function () {
31355 return this;
31356 };
31357 } // either .start() or .run() is defined, so autogen the other
31358
31359
31360 if (layoutProto.start && !layoutProto.run) {
31361 layoutProto.run = function () {
31362 this.start();
31363 return this;
31364 };
31365 } else if (!layoutProto.start && layoutProto.run) {
31366 layoutProto.start = function () {
31367 this.run();
31368 return this;
31369 };
31370 }
31371
31372 var regStop = registrant.prototype.stop;
31373
31374 layoutProto.stop = function () {
31375 var opts = this.options;
31376
31377 if (opts && opts.animate) {
31378 var anis = this.animations;
31379
31380 if (anis) {
31381 for (var _i = 0; _i < anis.length; _i++) {
31382 anis[_i].stop();
31383 }
31384 }
31385 }
31386
31387 if (regStop) {
31388 regStop.call(this);
31389 } else {
31390 this.emit('layoutstop');
31391 }
31392
31393 return this;
31394 };
31395
31396 if (!layoutProto.destroy) {
31397 layoutProto.destroy = function () {
31398 return this;
31399 };
31400 }
31401
31402 layoutProto.cy = function () {
31403 return this._private.cy;
31404 };
31405
31406 var getCy = function getCy(layout) {
31407 return layout._private.cy;
31408 };
31409
31410 var emitterOpts = {
31411 addEventFields: function addEventFields(layout, evt) {
31412 evt.layout = layout;
31413 evt.cy = getCy(layout);
31414 evt.target = layout;
31415 },
31416 bubble: function bubble() {
31417 return true;
31418 },
31419 parent: function parent(layout) {
31420 return getCy(layout);
31421 }
31422 };
31423 extend(layoutProto, {
31424 createEmitter: function createEmitter() {
31425 this._private.emitter = new Emitter(emitterOpts, this);
31426 return this;
31427 },
31428 emitter: function emitter() {
31429 return this._private.emitter;
31430 },
31431 on: function on(evt, cb) {
31432 this.emitter().on(evt, cb);
31433 return this;
31434 },
31435 one: function one(evt, cb) {
31436 this.emitter().one(evt, cb);
31437 return this;
31438 },
31439 once: function once(evt, cb) {
31440 this.emitter().one(evt, cb);
31441 return this;
31442 },
31443 removeListener: function removeListener(evt, cb) {
31444 this.emitter().removeListener(evt, cb);
31445 return this;
31446 },
31447 removeAllListeners: function removeAllListeners() {
31448 this.emitter().removeAllListeners();
31449 return this;
31450 },
31451 emit: function emit(evt, params) {
31452 this.emitter().emit(evt, params);
31453 return this;
31454 }
31455 });
31456 define$3.eventAliasesOn(layoutProto);
31457 ext = Layout; // replace with our wrapped layout
31458 } else if (type === 'renderer' && name !== 'null' && name !== 'base') {
31459 // user registered renderers inherit from base
31460 var BaseRenderer = getExtension('renderer', 'base');
31461 var bProto = BaseRenderer.prototype;
31462 var RegistrantRenderer = registrant;
31463 var rProto = registrant.prototype;
31464
31465 var Renderer = function Renderer() {
31466 BaseRenderer.apply(this, arguments);
31467 RegistrantRenderer.apply(this, arguments);
31468 };
31469
31470 var proto = Renderer.prototype;
31471
31472 for (var pName in bProto) {
31473 var pVal = bProto[pName];
31474 var existsInR = rProto[pName] != null;
31475
31476 if (existsInR) {
31477 return overrideErr(pName);
31478 }
31479
31480 proto[pName] = pVal; // take impl from base
31481 }
31482
31483 for (var _pName in rProto) {
31484 proto[_pName] = rProto[_pName]; // take impl from registrant
31485 }
31486
31487 bProto.clientFunctions.forEach(function (name) {
31488 proto[name] = proto[name] || function () {
31489 error('Renderer does not implement `renderer.' + name + '()` on its prototype');
31490 };
31491 });
31492 ext = Renderer;
31493 }
31494
31495 return setMap({
31496 map: extensions,
31497 keys: [type, name],
31498 value: ext
31499 });
31500}
31501
31502function getExtension(type, name) {
31503 return getMap({
31504 map: extensions,
31505 keys: [type, name]
31506 });
31507}
31508
31509function setModule(type, name, moduleType, moduleName, registrant) {
31510 return setMap({
31511 map: modules,
31512 keys: [type, name, moduleType, moduleName],
31513 value: registrant
31514 });
31515}
31516
31517function getModule(type, name, moduleType, moduleName) {
31518 return getMap({
31519 map: modules,
31520 keys: [type, name, moduleType, moduleName]
31521 });
31522}
31523
31524var extension = function extension() {
31525 // e.g. extension('renderer', 'svg')
31526 if (arguments.length === 2) {
31527 return getExtension.apply(null, arguments);
31528 } // e.g. extension('renderer', 'svg', { ... })
31529 else if (arguments.length === 3) {
31530 return setExtension.apply(null, arguments);
31531 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse')
31532 else if (arguments.length === 4) {
31533 return getModule.apply(null, arguments);
31534 } // e.g. extension('renderer', 'svg', 'nodeShape', 'ellipse', { ... })
31535 else if (arguments.length === 5) {
31536 return setModule.apply(null, arguments);
31537 } else {
31538 error('Invalid extension access syntax');
31539 }
31540}; // allows a core instance to access extensions internally
31541
31542
31543Core.prototype.extension = extension; // included extensions
31544
31545incExts.forEach(function (group) {
31546 group.extensions.forEach(function (ext) {
31547 setExtension(group.type, ext.name, ext.impl);
31548 });
31549});
31550
31551// (useful for init)
31552
31553var Stylesheet = function Stylesheet() {
31554 if (!(this instanceof Stylesheet)) {
31555 return new Stylesheet();
31556 }
31557
31558 this.length = 0;
31559};
31560
31561var sheetfn = Stylesheet.prototype;
31562
31563sheetfn.instanceString = function () {
31564 return 'stylesheet';
31565}; // just store the selector to be parsed later
31566
31567
31568sheetfn.selector = function (selector) {
31569 var i = this.length++;
31570 this[i] = {
31571 selector: selector,
31572 properties: []
31573 };
31574 return this; // chaining
31575}; // just store the property to be parsed later
31576
31577
31578sheetfn.css = function (name, value) {
31579 var i = this.length - 1;
31580
31581 if (string(name)) {
31582 this[i].properties.push({
31583 name: name,
31584 value: value
31585 });
31586 } else if (plainObject(name)) {
31587 var map = name;
31588 var propNames = Object.keys(map);
31589
31590 for (var j = 0; j < propNames.length; j++) {
31591 var key = propNames[j];
31592 var mapVal = map[key];
31593
31594 if (mapVal == null) {
31595 continue;
31596 }
31597
31598 var prop = Style.properties[key] || Style.properties[dash2camel(key)];
31599
31600 if (prop == null) {
31601 continue;
31602 }
31603
31604 var _name = prop.name;
31605 var _value = mapVal;
31606 this[i].properties.push({
31607 name: _name,
31608 value: _value
31609 });
31610 }
31611 }
31612
31613 return this; // chaining
31614};
31615
31616sheetfn.style = sheetfn.css; // generate a real style object from the dummy stylesheet
31617
31618sheetfn.generateStyle = function (cy) {
31619 var style = new Style(cy);
31620 return this.appendToStyle(style);
31621}; // append a dummy stylesheet object on a real style object
31622
31623
31624sheetfn.appendToStyle = function (style) {
31625 for (var i = 0; i < this.length; i++) {
31626 var context = this[i];
31627 var selector = context.selector;
31628 var props = context.properties;
31629 style.selector(selector); // apply selector
31630
31631 for (var j = 0; j < props.length; j++) {
31632 var prop = props[j];
31633 style.css(prop.name, prop.value); // apply property
31634 }
31635 }
31636
31637 return style;
31638};
31639
31640var version = "3.19.1";
31641
31642var cytoscape = function cytoscape(options) {
31643 // if no options specified, use default
31644 if (options === undefined) {
31645 options = {};
31646 } // create instance
31647
31648
31649 if (plainObject(options)) {
31650 return new Core(options);
31651 } // allow for registration of extensions
31652 else if (string(options)) {
31653 return extension.apply(extension, arguments);
31654 }
31655}; // e.g. cytoscape.use( require('cytoscape-foo'), bar )
31656
31657
31658cytoscape.use = function (ext) {
31659 var args = Array.prototype.slice.call(arguments, 1); // args to pass to ext
31660
31661 args.unshift(cytoscape); // cytoscape is first arg to ext
31662
31663 ext.apply(null, args);
31664 return this;
31665};
31666
31667cytoscape.warnings = function (bool) {
31668 return warnings(bool);
31669}; // replaced by build system
31670
31671
31672cytoscape.version = version; // expose public apis (mostly for extensions)
31673
31674cytoscape.stylesheet = cytoscape.Stylesheet = Stylesheet;
31675
31676module.exports = cytoscape;